打印

STM32的SPI1 DMA通道读取SPI flash,有若干细节问题处理及讨论

[复制链接]
23476|27
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
warden|  楼主 | 2010-12-16 08:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
程序实现结果:
     用stm32的一个SPI,用DMA方式读取spi flsh中的内容,然后将读到的数据用usart1 dma发到PC上,baud:115200;

选用的FLASH:W25X40  4M bit

电路接法:
     stm32的spi1的四只引脚分别与flash的四只引脚一一对就就好

程序:
    见附件

以下为这两天的调试过程:(可以略过)
     刚开始自己把它想得似乎过于简单点了,以为只要用spi占用dma通道进行读取就行,实际上做起来问题来了?我要读取flash中的某个地址开始的一段数据,得先把命令和地址发过去给flash呀,不光是发,发了还要收呀,难道要用两个spi?spi1负责发送命令,spi2负责接收数据?纠结了会,正当要开始按这种思路开始的时候,思路终于来了。。。。

我的思路:
    用SPI1-tx发送命令,同时用SPI1-rx接收数据;

程序中的几个细节处理:
    1>定义一个地址和命令的发送数组: Flash_Add_Table[4],用来配置spi1-tx dma的reg地址;存放的是要发送的命令和地址信息,每次发送前将里面的内容更新,如:它的值为:0x0b 0x00 0x00 0x00,那么它代表的意思就是命令0x0b,开始地址为0x000000;(0x0b为flash的快速读取命令,地址宽度为3字节);
     注意这里只定义数组大小为4,哪怕是读取几千个字节它也是4,因为读取的时候只有前面命令字节和地址信息才有用,之后的信息是没用的,哪怕它越界;
    2>定义一个接收数据数组:Usart_TX_Buf[FLASH_BUF_TOTAL_NUM],用来配置spi1-rx dma的reg地址,这里的FLASH_BUF_TOTAL_NUM包括了发命令时收到的数据(前5字节)、真正要读取的数据(N字节)、CRC值(1字节),详细情况看程序;
    3>每次接收完成后复位CRC的值,要不然会出错;
        SPI_CalculateCRC( SPI1,  DISABLE);
        SPI_CalculateCRC( SPI1,  ENABLE);
    4>spi作主机模式,NSS脚得注意了,这种读数据的场合NSS得用软件管理了,NSS脚用GPIO用来驱动flash上的片选信息;
     
     
    讨论点:
    1>本想用一个数据实现上述1,2功能,但始终只能正确接收一次,下午搞了好几个小时也没搞成,后来才换成2个数据来实现,原因:rx-dma tx-dma同一时间抢占一个地址延时??
     
    2>程序的内容是循环读0x000000开始的4K内容,刚开始只有第一次是正常,随后的就是接收到的数前面多了一个字节,spi-rx dma传完后还有字节,什么原因还没找到,应急就在每次rx-dma启动前加了下面语句:
        if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)!=RESET)
        {
            temp5=SPI1->DR;
        }
      这个地方还待改善;
    3>接收一次后发现SPI1_SR的CRCERR被置位了?我对过数据了,接收到的数据和flash中的数据是一样的,为什么会报CRC检验错误?怎么理解这个问题

手册中写道:
SPI带CRC的DMA功能  
当使能SPI使用CRC检验并且启用DMA模式时,在通信结束时,CRC字节的发送和接收是自动完成的。  
数据和CRC传输结束时,SPI_SR寄存器的CRCERR标志为’1’表示在传输期间发生错误。  

CRCERR:CRC错误标志 (CRC error flag)  位4  
0:收到的CRC值和SPI_RXCRCR寄存器中的值匹配;  
1:收到的CRC值和SPI_RXCRCR寄存器中的值不匹配。  
该位由硬件置位,由软件写’0’而复位。

既然是自动完成的,为什么报错?

USART-DMA测试.rar

1.28 MB

沙发
香水城| | 2010-12-16 09:47 | 只看该作者
CRC检验功能是通过计算数据流的CRC,再与发送方送来的CRC数值比较来检验数据的正确性;请问你的SPI Flash中存储了CRC数值并回送给STM32了吗?如果没有,当然会有CRCERR。

使用特权

评论回复
板凳
janeslee| | 2010-12-16 12:14 | 只看该作者
搞那么复杂,启动DMA之前先用SPI_SendByte这种方式把命令和地址发过去,然后根据读还是写分别启动DMA RX和DMA TX,DMA RX之前把SPI设置为SPI_Direction_2Lines_RxOnly,还要读一次SPIx->DR,以清除RXNE标志,避免DMA接收到的数据前面多一个无效字节。

等待FlashReady之前,一定要等到DMA传输完毕(即检查SPI_I2S_FLAG_TXE和SPI_I2S_FLAG_BSY标志),以免过早释放CS。另外完全不需要CRC检查。

使用特权

评论回复
地板
warden|  楼主 | 2010-12-16 12:37 | 只看该作者
谢谢香主~~

关于CRC,我的理解是这样:
如果我设定dma传输数据为10,发送方在发送完10byte后,是紧接着再发1byte的crc;
如果设定接收10byte,接收完第10byte后,会继续接收1byte作crc校验值,然后将这个值与通过硬件计算前10byte所生成的crc值(RXCRCR值)作比较,如果不同则出错;
不知这样理解是否正确?

使用特权

评论回复
5
warden|  楼主 | 2010-12-16 12:40 | 只看该作者
TO,3楼janeslee
按你这种思路,我回头好好试试;
这样是简单多了,谢谢啦!!!

使用特权

评论回复
6
myworkmail| | 2012-5-10 11:59 | 只看该作者
mark

使用特权

评论回复
7
mofan1| | 2013-2-25 22:00 | 只看该作者
香水城 发表于 2010-12-16 09:47
CRC检验功能是通过计算数据流的CRC,再与发送方送来的CRC数值比较来检验数据的正确性;请问你的SPI Flash中 ...

香主,你好,有个小小的问题想请教一下,希望你能百忙之中看一下哈,先谢谢了  关于STM32 SPI2 CRC校验的    关于这个的信息很少啊
我就发了一个帖子 那个帖子里有代码和逻辑分析仪的信号图   那个帖子没有人看哈  希望你能看下。

使用特权

评论回复
8
mofan1| | 2013-2-25 22:01 | 只看该作者
warden 发表于 2010-12-16 12:40
TO,3楼janeslee
按你这种思路,我回头好好试试;
这样是简单多了,谢谢啦!!! ...

你好 你的STM32 SPI CRC校验 做的怎么样了 是否成功 可否交流下

使用特权

评论回复
9
明月小厨| | 2013-3-10 19:49 | 只看该作者
   2>程序的内容是循环读0x000000开始的4K内容,刚开始只有第一次是正常,随后的就是接收到的数前面多了一个字节,spi-rx dma传完后还有字节,什么原因还没找到,应急就在每次rx-dma启动前加了下面语句:
        if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)!=RESET)
        {
            temp5=SPI1->DR;
        }
      这个地方还待改善;

这个原因是处理时,以DMA写SPI任务完成为结束标志;而写数据完成的时候,最后一个数据虽然不在发送数据寄存器(发送数据寄存器为空);但它还在移位寄存器中,并正在发送中......;
最后这一个正在发送的数据导致同时也将产生一个数据正在接收,并在接收完成后回写至接收数据寄存器;
而这个最后收到的数据,本次任务却没有处理;
但下一次任务的时候,就会莫名其妙"多"出来;

改正的办法是以接收数据完成为基准(如DMA读取通道的任务完成标志),判断任务是否完成;
SPI存在一级缓冲,即数据寄存器和移位寄存器;忽视了这个细节会引出许多麻烦;

使用特权

评论回复
10
zrzhouic| | 2013-10-27 23:56 | 只看该作者
   2>程序的内容是循环读0x000000开始的4K内容,刚开始只有第一次是正常,随后的就是接收到的数前面多了一个字节,spi-rx dma传完后还有字节,什么原因还没找到,应急就在每次rx-dma启动前加了下面语句:
        if(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE)!=RESET)
        {
            temp5=SPI1->DR;
        }
      这个地方还待改善;
关于这个问题,我是看SPI发送接受8位数据和16位数据的例程(到库函数里面看),我使用寄存器操作的写法,遇到这个问题,后来强制定义8位数据格式。下面程序是我根据8位数据发送接受库函数改的。或许楼主可以试下是否是这个原因。我的原程序未用DMA,这样做可以正常操作SPI FLASH。呵呵,现在在看是不是有人用DMA操作SPI FLASH才看到这个帖子。
uint32 spi1DRAdr=0X4001300C;
uint8 spi1_readwritebyte(uint8 TxData)
{               
//    uint32_t spi1DRAdr = (uint32_t)SPI1;

//        uint16 retry=0;               
//    spi1DRAdr+=0X0C;   
        while((SPI1->SR&SPI_SR_TXE)==0)                //等待发送区空       
        {
//                retry++;
//                if(retry>=0XFFFE)return 0;         //超时退出
        }                          
    *(__IO uint8_t *)spi1DRAdr =TxData;                                   //发送一个byte
//        retry=0;
        while((SPI1->SR&SPI_SR_RXNE)==0)                 //等待接收完一个byte  
        {
//                retry++;
//                if(retry>=0XFFFE)return 0;        //超时退出
        }                                                              
        return *(__IO uint8_t *) spi1DRAdr ;                          //返回收到的数据                                    
}

使用特权

评论回复
11
zrzhouic| | 2013-10-28 00:06 | 只看该作者
不好意思,没下楼主例程看,不清楚楼主程序怎么写的。希望不要误导楼主。

使用特权

评论回复
12
zrzhouic| | 2013-10-28 01:13 | 只看该作者
janeslee 发表于 2010-12-16 12:14
搞那么复杂,启动DMA之前先用SPI_SendByte这种方式把命令和地址发过去,然后根据读还是写分别启动DMA RX和D ...

已验证过,可行。应该要软件管理CS管脚,本来是硬件件管理CS的,直接改成软件管理。多谢多谢!!高手就是多啊。。
F051做LCD刷屏,直接从SPI Flash中取数据,不知道刷屏效果如何。。。。还要努力试。。。。

使用特权

评论回复
13
sinc_mark| | 2014-7-7 16:27 | 只看该作者
明月小厨 发表于 2013-3-10 19:49
2>程序的内容是循环读0x000000开始的4K内容,刚开始只有第一次是正常,随后的就是接收到的数前面多了一 ...

你好,我在调试DMA- SPI rx模式的时候,遇到这样的情况,我的spi 配置为主模式,收发两用,配置完DMA,运行一段时间,读回的数据正常,但过一段时间后,发现整一帧数据右移了3个字节,然后运行一段时间左移一字节,再运行一段时间再左移1字节,再运行一段时间再左移一字节,恢复正常情况,然后就是类似的循环。。
在中断中,发送完毕关闭发送中断,接收完毕关闭接收中断,下面是中断处理:
if(DMA_GetFlagStatus(DMA1_FLAG_TC4) == SET)
        {
                DMA_ClearITPendingBit(DMA1_FLAG_TC4 | DMA1_FLAG_HT4 |DMA1_FLAG_TE4);                //DMA1_FLAG_TC2 | DMA1_FLAG_HT2 | DMA1_FLAG_TE2
                DMA_Cmd(DMA1_Channel4, DISABLE);
                SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
                SPI2->DR;       
        }

        //发送完成
        if(DMA_GetITStatus(DMA1_IT_TC5) == SET)
        {
                DMA_ClearITPendingBit(DMA1_FLAG_TC5 | DMA1_FLAG_HT5 |DMA1_FLAG_TE5);

                DMA_Cmd(DMA1_Channel5, DISABLE);
                SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
        }

在主函数中开启相应的DMA请求:
SPI2->DR;
                       
                        DMA_Cmd(DMA1_Channel5, DISABLE);
                        DMA_ClearFlag(DMA1_FLAG_TC5 |DMA1_FLAG_HT5 | DMA1_FLAG_TE5);
                        DMA_SetCurrDataCounter(DMA1_Channel5,0);
                        DMA_SetCurrDataCounter(DMA1_Channel5,64);
                        DMA1_Channel5->CMAR  = (uint32_t)dummy;
                        SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, DISABLE);
                        DMA_Cmd(DMA1_Channel5, ENABLE);
                        if( SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE))
                        {
                                SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
                        }


                        DMA_Cmd(DMA1_Channel4, DISABLE);
                       
                        DMA_ClearFlag(DMA1_FLAG_TC4 |DMA1_FLAG_HT4 | DMA1_FLAG_TE4);
                        DMA_SetCurrDataCounter(DMA1_Channel4,0);
                        DMA_SetCurrDataCounter(DMA1_Channel4,64);
                        DMA1_Channel4->CMAR  = (uint32_t)RfBuf;
                        SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, DISABLE);
                        DMA_Cmd(DMA1_Channel4, ENABLE);
                        SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);
这个问题折腾了几天了,请指教,谢谢!





使用特权

评论回复
14
sweatsurrender| | 2014-7-7 23:20 | 只看该作者
3楼说的是最方便的用法。我用DMA也都这样用滴。

使用特权

评论回复
15
kuyu20| | 2014-8-30 15:34 | 只看该作者
学习了,正想这样用!

使用特权

评论回复
16
ylp09| | 2014-12-29 22:04 | 只看该作者
碰到类似问题,今天没时间了,明天再研究研究

使用特权

评论回复
17
zh113214| | 2014-12-30 19:12 | 只看该作者
用SPI1-tx发送命令,同时用SPI1-rx接收数据确实是不错的想法

使用特权

评论回复
18
STM32初学者| | 2014-12-30 20:34 | 只看该作者
向大家学习一下

使用特权

评论回复
19
STM32初学者| | 2014-12-30 20:34 | 只看该作者
向大家学习一下

使用特权

评论回复
20
小浣熊| | 2014-12-31 12:05 | 只看该作者
我用DMA也都和三楼一样。。。

使用特权

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

本版积分规则

个人签名:低调 淡定

11

主题

47

帖子

1

粉丝