打印
[STM32F1]

STM32F103 SPI DMA从机通讯的问题。

[复制链接]
4273|15
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本意是想做两片STM32 SPI 双机通讯。 现在通过网购的一个SPI转USB做SPI主站来调试我其中一块板子,板子上的STM32做
做SPI从站。实验方法是SPI转USB的设备发送24个字节给stm32,同时stm32返回24个字节其中最后一个字节是加和校验。
当作为从机的stm32间隔一定时间没有收到数据则应该重新初始化自己的SPI发送缓冲区。实验结果如下图
颜色不一致的是两次不同的接收。
可以看到当stm32收到第一个24字节数据后返回的数据0x01至0x17再加上一个字节0x14加和校验一共是24个字节 是正确的。
但是当间隔一定时间再次收到第二组24个字节后返回的是0x01 0x01 0x02.....0x17。可以看到收到了两次0x01。收到第三组24个字节后返回的数据是
0x14 0x01......0x17。虽然stm32通过定时器判断 长时间没有接收数据后就想通过程序初始化DMA发送缓冲区。但是我初始化后还是会出现上面的问题。
我分析是作为从机的stm32 的SPI-DR发送没有清除。因为stm32上电第一次运行时SPI-DR发送是空的 所以第一次发送的数据是对的(对应下图中第一组数据0x01....0x14)。经过间隔收到的第二组数据前面多了一个0x01的原因是第一次发送完数据后下一次应该发送的是0x01 虽然我做了初始化但是没有清除SPI-DR的值 所以当再次发送时 先把上次缓存再SPI-DR的值0x01 发送出去然后发送接下来的0x01...0x17。后面的数据也是这个逻辑。
现在我想问。我如何才能清除SPI-DR的发送缓存让每次都能从SPI发送的内存里重新取数据。
再判断长时间没有收到数据的TIM2的中断函数里做了如下程序
                                memcpy(SPI2_TxBuf,SPI2_TxBuftest,sizeof(SPI2_TxBuf));//将AD滤波之后的值放入SPI2 DMA发送地址
                                CheckSumSPI2=CheckSum_CalcSPI2(SPI2_TxBuf,sizeof(SPI2_TxBuf)-1);
                                *(SPI2_TxBuf+SPI2_DMA_RECE_SHIFT_Val.CheckSumShift)=CheckSumSPI2;
                                SPI_DMA_Config();

意为把SPI 发送DMA的内存取数据更新然后执行SPI_DMA_Config();重新初始化SPI2的DMA
SPI_DMA_Config()函数内容如下
void SPI_DMA_Config(void)  
{     
GPIO_InitTypeDef GPIO_InitStructure;  
SPI_InitTypeDef  SPI_InitStructure;  
DMA_InitTypeDef  DMA_Initstructure;  
NVIC_InitTypeDef NVIC_InitStructure;//NVIC初始化结构体
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);  
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE );   
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);  
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_CRC, ENABLE);  
CRC_ResetDR();   
/* 设置 SPI2 引脚: SCK, MISO 和 MOSI */  
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;  
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
GPIO_Init(GPIOB, &GPIO_InitStructure);  



DMA_DeInit(DMA1_Channel4);//SPI2对应的DMA通道数  
DMA_Initstructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;//外设地址(u32)SPI2_DR_Address;  
DMA_Initstructure.DMA_MemoryBaseAddr = (u32)&SPI2_RxBuf;//内存地址,就是你想要把采样值存在那个变量的地址  
//DMA_Initstructure.DMA_MemoryBaseAddr = (u32)(&SPI2_RxBuf+tmp22/*SPI2_DMA_RECE_SHIFT_Val.MachineTypeShift*/);
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC ;//方向  
DMA_Initstructure.DMA_BufferSize = SPI2_RxDataLength;//-tmp22/*SPI2_DMA_RECE_SHIFT_Val.MachineTypeShift*/;//开辟SPI2_RxDataLength个连续的DMA存储单元  
DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable ;//外设地址不变  
DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable ;//内存地址自增  
DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte ;//设置外设数据长度为子字节
DMA_Initstructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte ;//设置DMA存储数据长度为字节
DMA_Initstructure.DMA_Mode = DMA_Mode_Circular ;//循环模式  
DMA_Initstructure.DMA_Priority = DMA_Priority_VeryHigh ;//DMA优先级为高  
DMA_Initstructure.DMA_M2M = DMA_M2M_Disable ;  
DMA_Init(DMA1_Channel4 , &DMA_Initstructure  );  
DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
DMA_Cmd(DMA1_Channel4 , ENABLE );  


        DMA_DeInit(DMA1_Channel5);  
  DMA_Initstructure.DMA_PeripheralBaseAddr = (u32)&SPI2->DR;                          //设置  接收外设(0x4001300C) 地址(源地址)
  DMA_Initstructure.DMA_MemoryBaseAddr = (u32)&SPI2_TxBuf;                    //设置 SRAM 存储地址(源地址)
  DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralDST;                                //传输方向 内存-外设
  DMA_Initstructure.DMA_BufferSize = SPI2_TxDataLength;                           //设置 SPI1 接收长度
  DMA_Initstructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                  //外设地址增量(不变)
  DMA_Initstructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                           //内存地址增量(变化)
  DMA_Initstructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;           //外设传输宽度(字节)
  DMA_Initstructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                   //内存传输宽度(字节)
  DMA_Initstructure.DMA_Mode = DMA_Mode_Circular;                                     //传输方式,一次传输完停止,不重新加载
  DMA_Initstructure.DMA_Priority = DMA_Priority_VeryHigh;                           //中断方式-高(三级)
  DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;                                      //内存到内存方式禁止
  DMA_Init(DMA1_Channel5, &DMA_Initstructure);
  DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
  DMA_Cmd(DMA1_Channel5 , ENABLE );  



SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ;//两线全双工的通讯模式   
SPI_InitStructure.SPI_Mode = SPI_Mode_Slave ;//从机模式  
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b ;//数据格式为2进制8位  
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;//空闲时,时钟极性为低电平  
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二个时钟沿采集数据  
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;//配置NSS引脚为软件模式,一般一主一从的情况下用此模式  
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8 ;//设置波特率分频值为2,即18Mhz。因为SPI2挂在低速时钟APB1上  
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ;//设置高位数据在先  
SPI_InitStructure.SPI_CRCPolynomial = 7;//此处设置CRC校验中的多项式。本程序不用CRC校验,所以随便设了个值。  
SPI_Init(SPI2 , &SPI_InitStructure  );  

//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);


SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);//使能SPI DMA  
SPI_Cmd(SPI2 , ENABLE);//使能SPI2  
}  

谢谢



SPI.png (17.89 KB )

SPI.png
沙发
mmuuss586| | 2018-4-12 20:28 | 只看该作者
没发送完好像请不掉的;
重新启动SPI看看

使用特权

评论回复
板凳
antusheng| | 2018-4-13 15:40 | 只看该作者
双机通信不如用串口uart

使用特权

评论回复
地板
天灵灵地灵灵| | 2018-4-13 18:56 | 只看该作者
总是重复发第一个?

使用特权

评论回复
5
jiaojian|  楼主 | 2018-4-16 14:07 | 只看该作者
本帖最后由 jiaojian 于 2018-4-16 14:08 编辑
mmuuss586 发表于 2018-4-12 20:28
没发送完好像请不掉的;
重新启动SPI看看

必须要清除SPI->DR 因为如果不清的话 ,如果SCK受到干扰则后面的 全错位了 收到的都是乱码 重新启动 DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, DISABLE);                                   SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
                                  DMA_Cmd(DMA1_Channel5 , DISABLE ); 然后再ENABLE都用了 也还是那样啊

使用特权

评论回复
6
huzi2099| | 2018-4-16 18:21 | 只看该作者
你这段  SPI_DMA_Config();有问题,应该先配置好dma再启动spi,而且不用每次都初始化dma,enable一下就可以启动了.

使用特权

评论回复
7
jiaojian|  楼主 | 2018-4-17 07:56 | 只看该作者
huzi2099 发表于 2018-4-16 18:21
你这段  SPI_DMA_Config();有问题,应该先配置好dma再启动spi,而且不用每次都初始化dma,enable一下就可以启 ...

初始化没有问题  DMA完整初始化 在另一个地方写的 可以运行 没问题 我的问题是要在sck受干扰收到乱码的时候清除SPI->DR  如何清除的问题

使用特权

评论回复
8
huzi2099| | 2018-4-17 08:50 | 只看该作者
本帖最后由 huzi2099 于 2018-4-17 08:51 编辑
jiaojian 发表于 2018-4-17 07:56
初始化没有问题  DMA完整初始化 在另一个地方写的 可以运行 没问题 我的问题是要在sck受干扰收到乱码的时 ...

我觉得不需要考虑这个问题,SS信号线不使能的话数据是无效的,两帧之内必会有crc错误,数据作废就是了,等SS信号有效再重新开始就好了.
如果spi频繁出错那你还是找找硬件原因吧,不然没办法做了.

使用特权

评论回复
9
huzi2099| | 2018-4-17 08:55 | 只看该作者
你这个一帧对一帧错肯定不是sck干扰

使用特权

评论回复
10
huzi2099| | 2018-4-17 08:58 | 只看该作者
你没有SS线啊,这个要有的

使用特权

评论回复
11
jiaojian|  楼主 | 2018-4-17 15:38 | 只看该作者
huzi2099 发表于 2018-4-17 08:58
你没有SS线啊,这个要有的

我是没用NSS  线 我接了NSS能防止SCK受到干扰少 接到乱码吗   接上ss就能清除SPI-DR?我现在手动随意插拔spi的连接线模拟受到干扰的情况  接收到的就都是串bit的乱码  我程序怎么写都恢复不了  是串bit不是串字节。

使用特权

评论回复
12
huzi2099| | 2018-4-17 16:22 | 只看该作者
本帖最后由 huzi2099 于 2018-4-17 16:32 编辑
jiaojian 发表于 2018-4-17 15:38
我是没用NSS  线 我接了NSS能防止SCK受到干扰少 接到乱码吗   接上ss就能清除SPI-DR?我现在手动随意插拔s ...
重新初始化也不行吗?
你还是加上SS吧,不能清除DR,但最起码知道从哪个时钟开始接收啊.
你根本不知道从哪个时钟沿开始数据传输又怎么谈恢复
spi只能支持短距离通讯,基本就是板上芯片间,以速度快为目标,你要是想可靠远距或插拔还是换别的吧

使用特权

评论回复
13
jiaojian|  楼主 | 2018-4-18 09:24 | 只看该作者
huzi2099 发表于 2018-4-17 16:22
重新初始化也不行吗?
你还是加上SS吧,不能清除DR,但最起码知道从哪个时钟开始接收啊.
你根本不知道从哪个时 ...

我加上SS片选 作为从机的SPI受到主机片选控制 就能在帧开始 的时候清除上一次的SPI-DR的内容???假设上一帧由于收到干扰SCK少收了3个clk 那么 在下一帧的开始就要把上一次已经接到的5个sck和后来的3个sck组成了一个字节 因此导致错位。 我是两个芯片通信 在一块板子上的 我通过外部引线插拔的方式来模拟收到干扰发生错位的情况。因为现场有干扰。

使用特权

评论回复
14
huzi2099| | 2018-4-18 10:30 | 只看该作者
本帖最后由 huzi2099 于 2018-4-18 10:35 编辑
jiaojian 发表于 2018-4-18 09:24
我加上SS片选 作为从机的SPI受到主机片选控制 就能在帧开始 的时候清除上一次的SPI-DR的内容???假设上 ...

你说的这个没研究过,缺少clk下帧会有crc错误.
片选有效时加个什么语句再开始就行了.
伪读一下能清空不?需要做实验试试

使用特权

评论回复
15
jiaojian|  楼主 | 2018-4-18 12:41 | 只看该作者
huzi2099 发表于 2018-4-18 10:30
你说的这个没研究过,缺少clk下帧会有crc错误.
片选有效时加个什么语句再开始就行了.
伪读一下能清空不?需 ...

最后实验证明伪读不能清空 可以通过片选NSS的拉高再拉低来清空从机SPI-DR 如果不接NSS也可以 从机设置为SPI_NSS_Soft 后可以通过控制SPI-CR1的SSI位置1再归零来清空自己的SPI-DR 谢谢各位

使用特权

评论回复
16
huzi2099| | 2018-4-18 13:51 | 只看该作者
jiaojian 发表于 2018-4-18 12:41
最后实验证明伪读不能清空 可以通过片选NSS的拉高再拉低来清空从机SPI-DR 如果不接NSS也可以 从机设置为S ...

学习了

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

65

主题

196

帖子

0

粉丝