打印
[STM32]

STM32 AD采集的实时数据保存到nand flash中和定义大数组的问题

[复制链接]
9921|16
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
使用STM32的ADC1和ADC2同步采集2路外部信号,且定时器中断(TIM2_CC2,每0.01ms采集一次,即采样率为100ksps)触发ADC1和ADC2同时采集。
其中ADC1采集的数据保存在ADC_DR的低16位,ADC2采集的数据保存在ADC_DR的高16位。
然后将ADC1和ADC2采集的数据通过DMA方式将ADC_DR中的字数据传输至数组 unsigner int  AD_Value[ADC_BufferLength]中,最后将保存在数组
AD_Value中的数据写入nand flash中保存起来。
现在主要有两个问题:
问题1:由于数组AD_Value中保存的是字
数据,而目前所使用的nand  flash K9F1G08U0E是8位的,因此,在将数组AD_Value中的数据写入flash 之前,
需要先将数组AD_Value字数据分解字节数据,并将这些字节数据保存在字节数组unsigner char TxBuffer[ADC_BufferLength*4]中。
     for(i=0;i<ADC_BufferLength;i++)
            {
              TxBuffer[i*4]   = (u8)((AD_Value[i])&0xff);
              TxBuffer[i*4+1] = (u8)(((AD_Value[i])&0xff00)>>8);         //ADC1:低字
                     TxBuffer[i*4+2] = (u8)(((AD_Value[i])&0xff0000)>>16);
              TxBuffer[i*4+3] = (u8)(((AD_Value[i])&0xff000000)>>24);//ADC2:高字                       
                 }               
但是,这样势必影响到数据存储的速度,从而影响到实时数据的保存。该怎么办呢?
问题2:两路AD同步采集,且采集速率都是100ksps,而nand  flash的页编程时间一般为300us~700us,那么,在flash的页编程时间里,2路AD采集
到的数据量=2*100k*700us*2Byte(每个AD采集数据为2个字节)=280Byte,因此,为了能够保证保存AD采集的全部实时数据,那么缓冲区的大小
就至少为280Byte。
于是我就将ADC_BufferLength定义为128:#define ADC_BufferLength  128
这样一来,缓冲区的实际大小就是128*4=512Byte,这是满足要求的。
程序编译通过了,nand flash的读写也是正确的。但考虑到nand falsh的一页是2k=2048个字节,于是我将ADC_BuferLength定义为512,即2048个
字节,恰好是flash一页的大小。
编译能够通过,但是程序却一直在“字转字节”的小程序中陷入了死循环.
我尝试着将堆区扩大,将Stack_Size      EQU     0x00000800 改成 Stack_Size      EQU     0x00008000
结果依然是:在”字转字节“的小程序中死循环,全速运行,也不会到达断点处;
然后我将数组unsigner char TxBuffer[ADC_BufferLength*4]由局部变量改成全局变量,
结果依然是:在”字转字节“的小程序中陷入了死循环,即使全速运行,也不能到达断点处,当停止全速运行时,通过memory窗口
查看数组TxBuffer中数据时,发现,只能看到数据的前1024个数据,即TxBuffer[0]~TxBuffer[1023],之后的1024个数据则全部为
0x00。
这又该怎么办呢?

1.PNG (13.32 KB )

在"字转字节"的小程序中陷入了死循环

在"字转字节"的小程序中陷入了死循环

相关帖子

沙发
mazhao141| | 2015-3-18 19:18 | 只看该作者
本帖最后由 mazhao141 于 2015-3-18 19:30 编辑

问题1:对于时效要求特别高的程序应该用汇编写。这里涉及到数据结构问题。你会专门写一个字转字节的程序还为了它专门开辟一个BUFFER我觉得这种做法不合适
问题2:大数组是要受到SRAM大小和堆栈大小的限制。还有可能是其他原因。这么些只言片语我也确定不了到底是什么原因。我给你写一段汇编代码。你不要用这个字转字节的程序了,这个程序会从AD_Value中一次取一个字节并自动调整地址,然后直接送到对应的地址里。
                EXTERN AD_Value[] ;全局变量声明
                EXTERN NF_ADDR ;nandflash地址区的值

Start        
                ldr r0,=AD_Value
                ldr r1,=NF_ADDR
                add r2,r0,#2048
for
                ldrb r3,[r0],#1
                str r3,[r1]
                cmp r2,r0
                bne for               
                BX                 LR

使用特权

评论回复
板凳
mazhao141| | 2015-3-18 21:02 | 只看该作者
本帖最后由 mazhao141 于 2015-3-18 21:40 编辑

呃,如果你不会用 C和 汇编联编的话可能还比较麻烦。
我再教给你一个比较高端的技巧,用联合体声明你的AD_Value[512]
union
{
        unsigned int value_int[512];
        unsigned char value_char[2048];
}AD_Value;
这个做法,会强制让C将同一片地址区域解释成不同的数据类型
也就是说如果你AD_Value.value_int[0] = 0x11223344;那么就有
AD_Value.value_char[0] = 0x44
AD_Value.value_char[1] = 0x33
AD_Value.value_char[2] = 0x22
AD_Value.value_char[3] = 0x11一定要注意小端对齐的问题,按什么顺序写字节进去就按什么顺序读出来~
总之就是不要用你那个字转字节的程序了,我就不告诉你这个程序实际上会做什么事情了~~有点闹笑话~

使用特权

评论回复
地板
Mobile1991|  楼主 | 2015-3-19 19:56 | 只看该作者
mazhao141 发表于 2015-3-18 21:02
呃,如果你不会用 C和 汇编联编的话可能还比较麻烦。
我再教给你一个比较高端的技巧,用联合体声明你的AD_V ...

非常感谢您的帮助,我现在采用了您提供的第二种方法,即使用联合体(公用体)的方法。
从在谭浩强老师的《C++程序设计》这本书中,简单地学习了一下union这种数据类型。
所以以下的很多基本知识都是摘录:
1,“有时需要使几种不同类型的变量存放到同一段内存单元中。例如,可把一整型变量、一个字符型变量、一个双精度变量放在同一个地址开始的内存单元中,
而共用体(union)就是这种使几个不同的变量共占同一段内存的结构。“

2,”共用体变量所占的内存长度等于最长的成员的长度“。
3,”共用体类型数据的特点:
      (1)使用共用体变量的目的是希望用同一个内存段存放几种不同类型的数据。但请注意:在每一瞬时只能村存放其中一种,而不是同时存放集中。换句话说,
            每一瞬时,只有一个成员在起作用,其他成员不起作用。
      (2)能够访问的是共用体变量中最后一次被赋值的成员,在对一个新的成员赋值后原有的成员就失去作用。因此在引用共用体时应十分注意当前在共用体变量
           中起作用的究竟是哪个成员。
      (3)共用体变量的地址和它的成员的地址是同一地址。
      (4)不能对共用体变量名赋值;不能企图引用变量名来得到一个值;不能定义共用体变量时对它进行初始化;不能用共用体变量名作为函数参数。"


使用特权

评论回复
5
mazhao141| | 2015-3-19 20:11 | 只看该作者
本帖最后由 mazhao141 于 2015-3-19 20:19 编辑
Mobile1991 发表于 2015-3-19 19:56
非常感谢您的帮助,我现在采用了您提供的第二种方法,即使用联合体(公用体)的方法。
从在谭浩强老师的《C ...

我再给你两点建议,第一点把谭浩强的书撕了扔进垃圾桶。那简直就是误人子弟~第二点,要好好的使用指针这种东西。我送佛送到西吧,你按我说的声明好union 然后定义一个指针unsigned char *temp;
然后你把你的指针指向这个联合 temp =  AD_Value.value_char;
然后把你的写nandflash的函数接口定义成 int Write_nandflash(const unsigned char *addr); //大概定义,返回值和其他参数自己看着办
你可以直接把temp传进函数,并且使用*(temp++)这种东西。你自己试试就知道了

使用特权

评论回复
6
yangwenguan| | 2015-3-20 08:14 | 只看该作者
请教nand初始化,读写代码, 来自哪里, 官方有提供吗?

使用特权

评论回复
7
Mobile1991|  楼主 | 2015-3-21 08:59 | 只看该作者
yangwenguan 发表于 2015-3-20 08:14
请教nand初始化,读写代码, 来自哪里, 官方有提供吗?

在网上找的

使用特权

评论回复
8
Mobile1991|  楼主 | 2015-3-21 09:00 | 只看该作者
mazhao141 发表于 2015-3-19 20:11
我再给你两点建议,第一点把谭浩强的书撕了扔进垃圾桶。那简直就是误人子弟~第二点,要好好的使用指针这种 ...

谢谢

使用特权

评论回复
9
Mobile1991|  楼主 | 2015-3-21 11:51 | 只看该作者
在坛友的帮助下,使用了union 类型
typedef union
{
unsigned int Value_int[512];
unsigned char Value_char[2048];
}ADC_Value;
ADC_Value AD
ADC1和ADC2每采集一次,DMA就立即将该数据传输给数组AD1.Value_int,
当512个字全部传输完毕,进入DMA中断;
DMA中断程序主要是将刚刚采集到的512个字,即2048个字节写入flash中。
关于“在DMA中断函数中完成写flash”,这是我自己比较惆怅的地方:
之所以很难保证AD采集数据的完整性,flash的存储速度跟不上就是主要的原因。
而现在在中断程序中完成flash写操作,除去flash页编程的时间不说,还有额外的进入中断和
退出中断的时间。这无疑是雪上加霜啊。   
ADC1和ADC2的同步采集是由定时器2中断(IM2_CC)触发的,采样率控制为100ksps,也就是   
说,采样的时间间隔是10us。
而当开始第一次采集到一次写flash完成的时间,设为T,则     
T=AD采集时间+DMA传输时间(虽然时间很短很短)+进入DMA中断时间+在中断中写flash时间+退出DMA中断时间。
T应该是大于10us,所以实际写入flash的数据不是AD第一次采集的数据。因为AD1_Value_int中的数据每10us就会改变一次,而T却比10us大,所以,实际写入flash的数据也不知道是AD第几次采集的。
除非一进入DMA中断,就关闭定时器或者关闭DMA传输,那么写入flash的数据则是关闭定时器之前时AD采集的数据。
但是这样的话,AD的采集频率就不再是100ksps,肯定比100ksps小。
所以,我现在在考虑:
(1)不要在DMA中断函数中完成flash写操作,但不在DMA中断函数中写,又该怎么弄呢?
(2)坛友提到的“双缓冲模式”,定义两个缓冲区,一个缓冲区存满之后,就存第二个缓冲区,同时将第一个缓冲区中数据写入flash中;等第二个缓冲区满了,重新回来存第一个缓冲区,同时,将第二个缓冲区中的数据写入flash中。                  
用两个缓冲区交替工作来保证采集的连续性。道理是明白的,但程序实现上,还是挺茫然的。

使用特权

评论回复
10
mazhao141| | 2015-3-21 17:58 | 只看该作者
本帖最后由 mazhao141 于 2015-3-24 11:57 编辑
Mobile1991 发表于 2015-3-21 11:51
在坛友的帮助下,使用了union 类型
typedef union
{


使用特权

评论回复
11
panjuqi2015| | 2015-4-9 19:58 | 只看该作者
我也想过交替存放adc数据,但没那么大的缓冲区,最多是20个字大小而已,在SRAM中开辟,只是将获得的数据及时运算处理掉,没想过要存放太久时间

使用特权

评论回复
12
yu515301489| | 2015-8-27 09:57 | 只看该作者
mark以下再学习,这问题困扰久了……

使用特权

评论回复
13
yu515301489| | 2015-8-27 10:06 | 只看该作者
mazhao141 发表于 2015-3-18 21:02
呃,如果你不会用 C和 汇编联编的话可能还比较麻烦。
我再教给你一个比较高端的技巧,用联合体声明你的AD_V ...

这个方法好,浮点数什么的都这样存。

使用特权

评论回复
14
z1303087| | 2016-1-4 21:37 | 只看该作者
学习一下

使用特权

评论回复
15
耶耶5555| | 2016-7-15 10:16 | 只看该作者
学习学习

使用特权

评论回复
16
aliyaolei| | 2018-2-3 15:17 | 只看该作者
本帖最后由 aliyaolei 于 2018-8-20 16:18 编辑
Mobile1991 发表于 2015-3-21 11:51
在坛友的帮助下,使用了union 类型
typedef union
{

楼主这个问题我也遇到了,双缓冲我是这样实现的:
首先定义两个ADC数据缓冲数组:ADCData1[]、ADCData2[],及两个指向数组的指针*ADCdataSelect,*ADCdataReady用来指向正在写的缓冲数组和已经存满的缓冲数组。
指针初始化:ADCdataSelect=ADCdata1,ADCdataReady=NULL。(数组选择指针ADCdataSelect先指向ADC数据缓冲数组1,数组存满指针ADCdataReady赋NULL。)
先往数组选择指针ADCdataSelect指向的数组存数据,存满后将该数组的地址赋给数组存满指针ADCdataReady,而数组选择指针ADCdataSelect指向另一个数组。
然后用DMA将数组存满指针ADCdataReady指向的数组的数据传给NAND进行写入,同时ADC数据采集的数据存在数组选择指针ADCdataSelect指向的数组,NAND写入完毕后,将数组存满指针ADCdataReady清零(NULL)。
往复循环即可。

使用特权

评论回复
17
RoxyKKo| | 2021-12-28 12:59 | 只看该作者
学习到了,谢谢,mark一下

使用特权

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

本版积分规则

11

主题

45

帖子

1

粉丝