OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5535|回复: 1

STM32F407+ad7606+fsmc高速采集并实时存储的问题

[复制链接]

0

主题

1

帖子

0

精华

新手入门

积分
14
金钱
14
注册时间
2021-5-27
在线时间
6 小时
发表于 2021-5-31 18:57:35 | 显示全部楼层 |阅读模式
本帖最后由 你给的安然c 于 2021-6-1 09:56 编辑

最近在做一个数据采集的项目,用的是STM32F407和8通道同步采样adc ad7606,要求实时记录采集的ADC值,网上找了找用fsmc 实现ad7606采集的程序,目前能够实现8个通道的单次采样和以某个采样率(比如10k,50K,100K等等)自动采样。
上面的图是7606手册上的读取数据的时序图,产生PWM波的引脚接了7606的convstAB,PC5接了7606的busy,再将PC5配置成外部中断,在外部中断里面通过FSMC将7606转换的数据读出来(时间大概1us再加上存数组可能2us),就可以实现以某个采样率自动采样。但问题出在数据存储上,假设我采样率设置为50K,那么8通道的数据加起来会达到800KB/s,这要求带文件系统的情况下写SD卡的速度要大于800KB/s,同时意味着每隔20us将进一次外部中断读取数据,因此,我的想法是设置两个缓冲数组,两个数组在中断里面交替接收数据(buf1满了,将ADC值存到buf2,buf2满了将ADC值存到buf1,类似于双缓冲),在主程序中将存满的缓冲数组通过文件系统写到SD卡中,其中,缓冲数组大小为4096字节,这样相当于一个缓冲数组就可以接收256次ad7606的数据(ad7606的adc是16位),这也相当于为主程序写SD卡争取了256*(20-1)=4864us。我在外部中断里面增加了缓冲数组溢出判断,假设buf1存满了,buf1满标志置为1,之后进外部中断adc值将存到buf2,这个时候主程序能在4864us内写完sd卡,会将把buf1满标志置0,代表buf1已准备好,buf2满了后又可以换成buf1继续接收。但下载后运行,他会一直提示溢出,我把采样率降低获取更多时间,或者提高缓冲数组大小,同样存在这个问题。我暂时没想明白问题出在哪,可能是逻辑错误,希望大佬们帮我看看。整体的代码是在原子的录音机实验上改的,部分代码在下面:



typedef struct
{
        uint8_t ucOS;                        /* 过采样 */
        uint8_t ucRange;                /* 量程*/
        int16_t sNowAdc[8];                /* 当前ADC值 */
}AD7606_VAR_T;

/********************FIFO结构体********************/
typedef struct
{
        /* FIFO参数*/
        uint16_t usRead;                /* 读指针*/
        uint16_t usWrite;                /* 写指针*/

        uint16_t usCount_Bytes;                /* 计数 */

        uint8_t UseFiof1_flag;                /* FIFO使用标志 */
        uint8_t UseFiof2_flag;
        
        uint8_t Fifo1Full_flag;                /* FIFO满标志 */
        uint8_t Fifo2Full_flag;
        
        uint8_t FullDiasable1;         /*FIFO*/
}AD7606_FIFO_T;


u8  *sBuf_Adc1;
u8  *sBuf_Adc2;
u8  *sBuf_HexToStr1;
u8  *sBuf_HexToStr2;


/**********外部中断里面的ADC读取代码***********/
void AD7606_ISR_new(void)
{
        uint8_t i;

        if(rec_sta==0X80)//录音模式
        {  
               
                AD7606_ReadNowAdc(); //读取ADC值   
               
                if(g_tAdcFifo.UseFiof1_flag == 1 && g_tAdcFifo.Fifo1Full_flag == 1)
                {
                        printf("FIFO1溢出\r\n");
                }
                if(g_tAdcFifo.UseFiof2_flag == 1 && g_tAdcFifo.Fifo2Full_flag == 1)
                {
                        printf("FIFO2溢出\r\n");
                }

                /******使用fifo1*******/
                if(g_tAdcFifo.UseFiof1_flag == 1 /*&& g_tAdcFifo.Fifo1Full_flag == 0*/)
                {
                        for (i = 0; i < 8; i++)
                        {               
                                sBuf_Adc1[g_tAdcFifo.usWrite] = g_tAD7606.sNowAdc & 0xff;  //存低8位
                                g_tAdcFifo.usWrite++;        
                                sBuf_Adc1[g_tAdcFifo.usWrite] = (g_tAD7606.sNowAdc>>8) & 0xff;   //存高8位     这里后面用了联合体定义,省去了将16位转为2个8位的时间,但还是会存在上面溢出的问题
                                g_tAdcFifo.usWrite++;
                        }
                        
                        if (g_tAdcFifo.usWrite >= ADC_FIFO_SIZE)     //4096字节  buf1存满
                        {
                                g_tAdcFifo.usWrite = 0;
                                g_tAdcFifo.Fifo1Full_flag = 1;              /* FIFO1满 */        
                                g_tAdcFifo.UseFiof1_flag = 0;                /* 关掉 FIFO1  */
                                g_tAdcFifo.UseFiof2_flag = 1;                /* 使用 FIFO2  */
                                g_tAdcFifo.FullDiasable1 = 1;              /*从上往下执行,避免buf1满后,立马写buf2*/
                        }
                        g_tAdcFifo.usCount_Bytes+=2;    /* 计数 */
                }  

                /******使用fifo2*******/         
                if(g_tAdcFifo.UseFiof2_flag == 1 && g_tAdcFifo.FullDiasable1 == 0)
                {
                        for (i = 0; i < 8; i++)
                        {
                                sBuf_Adc2[g_tAdcFifo.usWrite] = g_tAD7606.sNowAdc & 0xff;  //低8位
                                g_tAdcFifo.usWrite++;        
                                sBuf_Adc2[g_tAdcFifo.usWrite] = (g_tAD7606.sNowAdc>>8) & 0xff;   //高8位
                                g_tAdcFifo.usWrite++;
                        }        
                        
                        if (g_tAdcFifo.usWrite >= ADC_FIFO_SIZE)
                        {
                                g_tAdcFifo.usWrite = 0;
                                g_tAdcFifo.Fifo2Full_flag = 1;      /* FIFO2满 */
                                g_tAdcFifo.UseFiof2_flag = 0;                /* 关掉 FIFO2  */
                                g_tAdcFifo.UseFiof1_flag = 1;                /* 使用 FIFO1  */
                        }
                        g_tAdcFifo.usCount_Bytes+=2;    /*  计数 */
                }
                if(g_tAdcFifo.Fifo1Full_flag == 1)g_tAdcFifo.FullDiasable1 = 0;    //buf1满后  下一次中断 buf2可以使用
        }
}

/**********主程序中通过文件系统写SD卡***********/
void AD7606_ReadFifo(void)
{
        u8 res;
                if(g_tAdcFifo.Fifo1Full_flag == 1)
                {
                        HexToStr(sBuf_HexToStr1,sBuf_Adc1,ADC_FIFO_SIZE);        //这一步是转字符,之前以为写txt比写wav快 就试了一下,可以忽略
                        /************将FIFO1写到SD卡*************/
                        res = f_write(f_rec_txt, sBuf_HexToStr1, ADC_FIFO_SIZE*2, &bw);
                        if(res)printf("write error:%d\r\n",res);
                        /*************************************/               
                        INTX_DISABLE();//关掉总中断
                        g_tAdcFifo.Fifo1Full_flag = 0;
                        INTX_ENABLE();//开启总中断
                        wavsize+=ADC_FIFO_SIZE;
                }        
                if(g_tAdcFifo.Fifo2Full_flag == 1)
                {
                        HexToStr(sBuf_HexToStr2,sBuf_Adc2,ADC_FIFO_SIZE);        
                        /************将FIFO2写到SD卡*************/
                        res = f_write(f_rec_txt, sBuf_HexToStr2, ADC_FIFO_SIZE*2, &bw);
                        if(res)printf("write error:%d\r\n",res);
                        /*************************************/        
                        INTX_DISABLE();//关掉总中断
                        g_tAdcFifo.Fifo2Full_flag = 0;
                        INTX_ENABLE();//开启总中断
                        wavsize+=ADC_FIFO_SIZE;
                }
}

/**********下面是主程序***********/
void wav_recorder_new1(void)
{
        u8 res;
        u8 key;
        u8 rval=0;
        DIR recdir;                                                 //目录
         u8 *pname = 0;
        u8 timecnt=0;                                        //计时
        u32 recsec=0;                                        //录音时间
          while(f_opendir(&recdir,"0:/RECORDER"))//打开录音文件夹
         {         
                printf("RECORDER文件夹错误!\r\n");   
                delay_ms(200);                                 
                f_mkdir("0:/RECORDER");                                //创建文件夹
        }
/**************************分配内存***************************/
        f_rec_txt = (FIL *)mymalloc(SRAMIN,sizeof(FIL));               
        pname = mymalloc(SRAMIN,30);                                                
        sBuf_Adc1 = mymalloc(SRAMIN, ADC_FIFO_SIZE);               
        sBuf_Adc2 = mymalloc(SRAMIN, ADC_FIFO_SIZE);
        sBuf_HexToStr1 = mymalloc(SRAMIN, ADC_FIFO_SIZE*2);               
        sBuf_HexToStr2 = mymalloc(SRAMIN, ADC_FIFO_SIZE*2);

        if(!sBuf_Adc1||!sBuf_Adc2||!sBuf_HexToStr1||!sBuf_HexToStr2|| \
           !f_rec_txt|| \
           /*!wavhead|| */
           !pname)rval=1;         
        if(rval==0)               
        {
                recoder_enter_rec_mode();                      //这里我改了  仅仅显示按键信息
                pname[0]=0;                                        //pname没有任何文件名
                    while(rval==0)
                {
                        if(rec_sta==0X80)//录音模式
                        {
                                AD7606_ReadFifo();        //写sd卡
                        }
                        key=KEY_Scan(0);
                        switch(key)
                        {               
                                case KEY1_PRES:        //STOP&SAVE
                                        if(rec_sta&0X80)//有录音
                                        {
                                                rec_sta=0;        //关掉录音
                                                AD7606_StopRecord();        /*停止自动采集,关掉了外部中断 */
                                                f_close(f_rec_txt);
                                                wavsize=0;
                                                printf("录音结束\r\n");
                                        }
                                        rec_sta=0;
                                        recsec=0;
                                         LED1=1;                                                         //关闭DS1              
                                        break;         
                                case KEY0_PRES:        //REC/PAUSE
                                        if(rec_sta&0X01)//原来是暂停,继续录音
                                        {
                                                rec_sta&=0XFE;//取消暂停
                                        }else if(rec_sta&0X80)//已经是录音,暂停
                                        {
                                                rec_sta|=0X01;        //暂停
                                        }else                                //开始录音
                                        {
                                                recsec=0;         
                                                recoder_new_pathname_new1(pname);
                                                printf("录制:\r\n");        
                                                printf("%s\r\n",pname);//显示当前录音文件名字
                                                res = f_open(f_rec_txt,(const TCHAR*)pname, FA_CREATE_ALWAYS | FA_WRITE);
                                                if(res)
                                                {
                                                        printf("write error:%d\r\n",res);
                                                        rec_sta=0;        //创建文件失败,不能录音
                                                        rval=0XFE;        //提示SD卡是否存在
                                                }
                                                else
                                                {
                                                        recoder_msg_show(0,0);
                                                        rec_sta|=0X80;        //开始录音
                                                        AD7606_StartRecord(50000);                /*50khz采样*/
                                                }
                                        }                                                
                                 
                                        if(rec_sta&0X01)LED1=0;        //提示暂停
                                        else LED1=1;
                                        break;  
                        }
                        delay_ms(5);
                        timecnt++;
                        if((timecnt%20)==0)LED0=!LED0;//DS0闪烁  
                         if(recsec!=(wavsize/wavhead->fmt.ByteRate))        
                        {           
                                LED0=!LED0;//DS0闪烁
                                recsec=wavsize/wavhead->fmt.ByteRate;        更新时间
                                recoder_msg_show(recsec,wavhead->fmt.SampleRate*wavhead->fmt.NumOfChannels*wavhead->fmt.BitsPerSample);//更新显示
                        }
                }                 
        }   
                /********清内存*******/
                myfree(SRAMIN,f_rec_txt);        
                myfree(SRAMIN,pname);        
                myfree(SRAMIN,sBuf_Adc1);        
                myfree(SRAMIN,sBuf_Adc2);        
                myfree(SRAMIN,sBuf_HexToStr1);        
                myfree(SRAMIN,sBuf_HexToStr2);        
}

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
14
金钱
14
注册时间
2021-5-27
在线时间
6 小时
 楼主| 发表于 2021-6-2 09:22:12 | 显示全部楼层
我把主程序中将缓冲数组写到SD卡的部分,替换为延时函数,延时4ms,不会提示溢出,延时5ms以上,会提示溢出,验证了双缓冲思路应该是对的。问题在于SD卡通过文件系统写8192个字节时间大于4ms吗?还是在SD卡通过文件系统写数据不能频繁的被中断打断?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-27 17:22

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表