代码为寄存器版本功能描述:
ADC1工作在连续转换模式下,DMA设置为双缓存区模式,采集数据出现间断有规律的丢失:即每包64个Byte的数据,会出现连续两个包的数据前12个Bytes为0x00,接着两个包的数据完整,反复如此。请问这个问题是怎么导致的呢?
主函数代码如下:
//本代码用于测试ADC数据采集并485传输,写入SD卡
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "mpu.h"
#include "lcd.h"
#include "sdram.h"
#include "usart3.h"
#include "usart1.h"
#include "malloc.h"
#include "key.h"
#include "rs485.h"
#include "nand.h"
#include "ftl.h"
#include "w25qxx.h"
#include "sdmmc_sdcard.h"
#include "ff.h"
#include "exfuns.h"
#include "rtc.h"
#include "adc.h" //ADC
#include "dma.h"
#include "modules.h"
int main(void)
{
u8 led0sta=1;
u8 t=0,i=0;
u8 Start_MCL302[5] = {0x00,0x05,0x15,0xBD,0x61};
float temp;
UINT bw;
FIL fsrc; //File objects
FRESULT fr; //FatFs function common result code
//RTC
u8 hour,min,sec,ampm,year,month,date,week;
u8 tbuf[40];
u8 countmp[4];
u16 send_count = 0;
Stm32_Clock_Init(432,25,2,9);//设置时钟,216Mhz
delay_init(216); //延时初始化
uart1_init(13.5,9600); //初始化串口1波特率为9600 ,接收SVP数据
uart3_init(54,9600); //初始化串口3波特率为9600,接收磁罗盘数据
LED_Init(); //初始化与LED连接的硬件接口
MPU_Memory_Protection(); //保护相关存储区域
SDRAM_Init(); //初始化SDRAM
LCD_Init(); //初始化LCD
KEY_Init(); //按键初始化
Adc_DMA_Init(); //初始化ADC
RS485_Init(54,115200); //初始化RS485,设置usart2波特率为115200
W25QXX_Init(); //初始化W25Q256
my_mem_init(SRAMIN); //初始化内部内存池
my_mem_init(SRAMEX); //初始化外部内存池
my_mem_init(SRAMTCM); //初始化TCM内存池
//RTC时钟
RTC_Init(); //初始化RTC
POINT_COLOR=RED;
LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7");
LCD_ShowString(30,70,200,16,16,"RS485 TEST");
LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
POINT_COLOR=BLUE;//设置字体为蓝 {MOD}
//
while(SD_Init())//检测不到SD卡
{
delay_ms(500);
LED0(led0sta^=1);//DS0闪烁
}
FTL_Init();
exfuns_init(); //为fatfs相关变量申请内存
f_mount(fs[0],"0:",1); //挂载SD卡
RTC_Get_Time(&hour,&min,&sec,&m);
RTC_Get_Date(&year,&month,&date,&week);
sprintf((char*)tbuf,"0:%2d-%02d%02d%02d.txt",date,hour,min,sec); //利用RTC时钟生成文件名
fr = f_open(&fsrc,(char*)tbuf,FA_CREATE_ALWAYS|FA_WRITE|FA_READ); //打开或新建一个.txt文件,可读可写
//激活磁罗盘
for(t=0;t<5;t++)
{
USART3->TDR=Start_MCL302[t];
while((USART3->ISR&0X40)==0);//等待发送结束
}
//使用串口中断接收SVP、磁罗盘数据
// MYDMA_Config_R(DMA1_Stream1,4,(u32)&USART3->RDR,(u32)USART3_DMARX_BUF0,(u32)USART3_DMARX_BUF1,USART3_BUF_LEN);//USART3_TX:数据流3,4号通道;USART3_RX:数据流1,4号通道。
// MYDMA_Enable(DMA1_Stream1,USART3_BUF_LEN);//磁罗盘数DMA接收使能
//
MYDMA_ADC_Config(DMA2_Stream0,0,(u32)&ADC1->DR,(u32)ADC_DMARX_BUF0,(u32)ADC_DMARX_BUF1,ADC_BUF_LEN);//ADC1
MA2数据流0和4,通道0;ADC2
MA2数据流2和3,通道1;ADC3
MA2数据流0和1,通道2
MYDMA_Enable(DMA2_Stream0,ADC_BUF_LEN);//开启一次DMA传输
//RS485数据传输
MYDMA_Config_T(DMA1_Stream6,4,(u32)&USART2->TDR,(u32)rs485buf,RS485_TX_LEN);//USART2_TX:数据流6,4号通道;USART2_RX:数据流5,4号通道。
Get_DMA_Adc(ADC_CH5);
while(1)
{
if(ADC1->SR&(1<<5))
{
MYDMA_ADC_Config(DMA2_Stream0,0,(u32)&ADC1->DR,(u32)ADC_DMARX_BUF0,(u32)ADC_DMARX_BUF1,ADC_BUF_LEN);
ADC1->SR&=~(1<<5);//ADC_SR寄存器中ADC_OVER位清空
Get_DMA_Adc(ADC_CH5);
}
memcpy(countmp,&send_count,2);
rs485buf[RS485_TX_LEN-1] = countmp[0];
rs485buf[RS485_TX_LEN-2] = countmp[1];
RTC_Get_Time(&hour,&min,&sec,&m);//获取时钟信息
RTC_Get_Date(&year,&month,&date,&week);//
rs485buf[RS485_TX_LEN-6] = date;
rs485buf[RS485_TX_LEN-5] = hour;
rs485buf[RS485_TX_LEN-4] = min;
rs485buf[RS485_TX_LEN-3] = sec;
while(ADC_BUF_OK==0);
ADC_BUF_OK=0;
RS485_TX_Set(1); //设置为发送模式
MYDMA_Enable(DMA1_Stream6,RS485_TX_LEN);//开启一次DMA传输
//memset(rs485buf,0,RS485_TX_LEN); //clean the buf
f_sync(&fsrc); // 刷新文件存储区域的数据信息,将数据真正写入磁盘
//1M数据 8192
if(send_count<8192)
{
fr = f_write(&fsrc,rs485buf,RS485_TX_LEN,&bw);
f_sync(&fsrc); // 刷新文件存储区域的数据信息,将数据真正写入磁盘
send_count++;
while((DMA1->HISR&(1<<21))==0);//等待USART2发送结束
DMA1->HIFCR|=1<<21; //清除中断标志
RS485_RX_CNT=0;
RS485_TX_Set(0); //设置为接收模式
}
else
{
fr = f_write(&fsrc,rs485buf,RS485_TX_LEN,&bw);
while((DMA1->HISR&(1<<21))==0);//等待USART2发送结束
DMA1->HIFCR|=1<<21; //清除中断标志
RS485_RX_CNT=0;
RS485_TX_Set(0); //设置为接收模式
f_sync(&fsrc); // 刷新文件存储区域的数据信息,将数据真正写入磁盘
f_close(&fsrc);
RTC_Get_Time(&hour,&min,&sec,&m);
sprintf((char*)tbuf,"0:%2d-%02d%02d%02d.txt",date,hour,min,sec); //利用RTC时钟生成文件名
fr = f_open(&fsrc,(char*)tbuf,FA_CREATE_ALWAYS|FA_WRITE|FA_READ); //打开或新建一个.txt文件,可读可写
send_count=0;
}
}
}
DMA通道配置:
#include "dma.h"
#include "delay.h"
#include "sys.h"
#include "usart3.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//DMA 驱动代码
//正点原子@ALIENTEK
//技术论坛:
www.openedv.com
//创建日期:2016/7/14
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//////////////////////////////////////////////////////////////////////////////////
//当NVIC_Group为0时,NVIC_PreemptionPriority必须为0,NVIC_SubPriority可以为0~15
//当NVIC_Group为1时,NVIC_PreemptionPriority可以为0~1,NVIC_SubPriority可以为0~7
//当NVIC_Group为2时,NVIC_PreemptionPriority可以为0~3,NVIC_SubPriority可以为0~3
//当NVIC_Group为3时,NVIC_PreemptionPriority可以为0~7,NVIC_SubPriority可以为0~1
//当NVIC_Group为4时,NVIC_PreemptionPriority可以为0~15,NVIC_SubPriority必须为0
//DMAx的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx
MA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//chx
MA通道选择,范围:0~7
//par:外设地址
//mar:存储器地址
//ndtr:数据传输量
u8 USART3_DMARX_BUF0[USART3_BUF_LEN];
u8 USART3_DMARX_BUF1[USART3_BUF_LEN];
u16 ADC_DMARX_BUF0[ADC_BUF_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
u16 ADC_DMARX_BUF1[ADC_BUF_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
u8 USRAT3_BUF_OK=0;
u8 ADC_BUF_OK=0;
u8 rs485buf[RS485_TX_LEN];
void MYDMA_Config_T(DMA_Stream_TypeDef *DMA_Streamx,u8 chx,u32 par,u32 mar0,u16 ndtr)
{
DMA_TypeDef *DMAx;
u8 streamx;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
DMAx=DMA2;
RCC->AHB1ENR|=1<<22;//DMA2时钟使能
}else
{
DMAx=DMA1;
RCC->AHB1ENR|=1<<21;//DMA1时钟使能
}
while(DMA_Streamx->CR&0X01);//等待DMA可配置
streamx=(((u32)DMA_Streamx-(u32)DMAx)-0X10)/0X18; //得到stream通道号
if(streamx>=6)DMAx->HIFCR|=0X3D<<(6*(streamx-6)+16); //清空之前该stream上的所有中断标志
else if(streamx>=4)DMAx->HIFCR|=0X3D<<6*(streamx-4); //清空之前该stream上的所有中断标志
else if(streamx>=2)DMAx->LIFCR|=0X3D<<(6*(streamx-2)+16);//清空之前该stream上的所有中断标志
else DMAx->LIFCR|=0X3D<<6*streamx; //清空之前该stream上的所有中断标志
DMA_Streamx->
AR=par; //DMA外设地址
DMA_Streamx->M0AR=mar0; //DMA 存储器0地址
DMA_Streamx->NDTR=ndtr; //DMA 数据项数
DMA_Streamx->CR=0; //先全部复位CR寄存器值
DMA_Streamx->CR|=1<<6; //存储器到外设模式
DMA_Streamx->CR|=0<<8; //非循环模式
DMA_Streamx->CR|=0<<9; //外设非增量模式
DMA_Streamx->CR|=1<<10; //存储器增量模式
DMA_Streamx->CR|=0<<11; //外设数据长度:8位
DMA_Streamx->CR|=0<<13; //存储器数据长度:8位
DMA_Streamx->CR|=1<<17; //高等优先级
DMA_Streamx->CR|=0<<21; //外设突发单次传输
DMA_Streamx->CR|=0<<23; //存储器突发单次传输
DMA_Streamx->CR|=(u32)chx<<25;//通道选择
//DMA_Streamx->FCR=0X21; //FIFO控制寄存器
}
void MYDMA_Config_R(DMA_Stream_TypeDef *DMA_Streamx,u8 chx,u32 par,u32 mar0,u32 mar1,u16 ndtr)
{
DMA_TypeDef *DMAx;
u8 streamx;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
DMAx=DMA2;
RCC->AHB1ENR|=1<<22;//DMA2时钟使能
}else
{
DMAx=DMA1;
RCC->AHB1ENR|=1<<21;//DMA1时钟使能
}
while(DMA_Streamx->CR&0X01);//等待DMA可配置
streamx=(((u32)DMA_Streamx-(u32)DMAx)-0X10)/0X18; //得到stream通道号
if(streamx>=6)DMAx->HIFCR|=0X3D<<(6*(streamx-6)+16); //清空之前该stream上的所有中断标志
else if(streamx>=4)DMAx->HIFCR|=0X3D<<6*(streamx-4); //清空之前该stream上的所有中断标志
else if(streamx>=2)DMAx->LIFCR|=0X3D<<(6*(streamx-2)+16);//清空之前该stream上的所有中断标志
else DMAx->LIFCR|=0X3D<<6*streamx; //清空之前该stream上的所有中断标志
DMA_Streamx->
AR=par; //DMA外设地址
DMA_Streamx->M0AR=mar0; //DMA 存储器0地址
DMA_Streamx->M1AR=mar1; //DMA 存储器1地址
DMA_Streamx->NDTR=ndtr; //DMA 数据项数
DMA_Streamx->CR=0; //先全部复位CR寄存器值
DMA_Streamx->CR|=0<<6; //外设到存储器模式
DMA_Streamx->CR|=1<<8; //循环模式,双缓存区模式下,自动使能循环模式
DMA_Streamx->CR|=0<<9; //外设非增量模式
DMA_Streamx->CR|=1<<10; //存储器增量模式
DMA_Streamx->CR|=0<<11; //外设数据长度:8位
DMA_Streamx->CR|=0<<13; //存储器数据长度:8位
DMA_Streamx->CR|=1<<17; //高等优先级
DMA_Streamx->CR|=1<<18; //双缓存区模式
DMA_Streamx->CR|=0<<21; //外设突发单次传输
DMA_Streamx->CR|=0<<23; //存储器突发单次传输
DMA_Streamx->CR|=(u32)chx<<25;//通道选择
//DMA_Streamx->FCR=0X21; //FIFO控制寄存器
DMA_Streamx->CR|=1<<4; //传输完成中断使能
MY_NVIC_Init(0,0,DMA1_Stream1_IRQn,2);//组2,最高优先级
}
void MYDMA_ADC_Config(DMA_Stream_TypeDef *DMA_Streamx,u8 chx,u32 par,u32 mar0,u32 mar1,u16 ndtr)
{
DMA_TypeDef *DMAx;
u8 streamx;
if((u32)DMA_Streamx>(u32)DMA2)//得到当前stream是属于DMA2还是DMA1
{
DMAx=DMA2;
RCC->AHB1ENR|=1<<22;//DMA2时钟使能
}else
{
DMAx=DMA1;
RCC->AHB1ENR|=1<<21;//DMA1时钟使能
}
while(DMA_Streamx->CR&0X01);//等待DMA可配置
streamx=(((u32)DMA_Streamx-(u32)DMAx)-0X10)/0X18; //得到stream通道号
if(streamx>=6)DMAx->HIFCR|=0X3D<<(6*(streamx-6)+16); //清空之前该stream上的所有中断标志
else if(streamx>=4)DMAx->HIFCR|=0X3D<<6*(streamx-4); //清空之前该stream上的所有中断标志
else if(streamx>=2)DMAx->LIFCR|=0X3D<<(6*(streamx-2)+16);//清空之前该stream上的所有中断标志
else DMAx->LIFCR|=0X3D<<6*streamx; //清空之前该stream上的所有中断标志
DMA_Streamx->
AR=par; //DMA外设地址
DMA_Streamx->M0AR=mar0; //DMA 存储器0地址
DMA_Streamx->M1AR=mar1; //DMA 存储器1地址
DMA_Streamx->NDTR=ndtr; //DMA 数据项数
DMA_Streamx->CR=0; //先全部复位CR寄存器值
DMA_Streamx->CR|=0<<6; //外设到存储器模式
DMA_Streamx->CR|=1<<8; //循环模式
DMA_Streamx->CR|=0<<9; //外设非增量模式
DMA_Streamx->CR|=1<<10; //存储器增量模式
DMA_Streamx->CR|=1<<11; //外设数据长度:16位
DMA_Streamx->CR|=1<<13; //存储器数据长度:16位
DMA_Streamx->CR|=1<<16; //中等优先级
DMA_Streamx->CR|=1<<18; //双缓冲区模式
DMA_Streamx->CR|=0<<21; //外设突发单次传输
DMA_Streamx->CR|=0<<23; //存储器突发单次传输
DMA_Streamx->CR|=(u32)chx<<25;//通道选择
//DMA_Streamx->FCR=0X21; //FIFO控制寄存器
DMA_Streamx->CR|=1<<4; //传输完成中断使能
MY_NVIC_Init(1,1,DMA2_Stream0_IRQn,2);//组2,最高优先级
}
//开启一次DMA传输
//DMA_Streamx
MA数据流,DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量
void MYDMA_Enable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
DMA_Streamx->CR&=~(1<<0); //关闭DMA传输
while(DMA_Streamx->CR&0X1); //确保DMA可以被设置
DMA_Streamx->NDTR=ndtr; //DMA 存储器0地址
DMA_Streamx->CR|=1<<0; //开启DMA传输
}
void DMA1_Stream1_IRQHandler(void)
{
u8 i=0;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(DMA1->LISR&(1<<11))//数据传输完成
{
DMA1->LIFCR|=1<<11;//清除中断状态
switch(DMA1_Stream1->CR&(1<<19))
{
case 0://DMA 存储器0地址被占用,将1中数据搬移
for(i=0;i<USART3_BUF_LEN;i++)
rs485buf
=USART3_DMARX_BUF1;
USRAT3_BUF_OK=1;
break;
case 0x80000://DMA 存储器1地址被占用,将0中数据搬移
for(i=0;i<USART3_BUF_LEN;i++)
rs485buf=USART3_DMARX_BUF0;
USRAT3_BUF_OK=1;
break;
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
void DMA2_Stream0_IRQHandler(void)
{
u8 i=0;
u8 len=0;
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntEnter();
#endif
if(DMA2->LISR&(1<<5))//数据传输完成
{
DMA2->LIFCR|=1<<5;//清除中断状态
switch(DMA2_Stream0->CR&(1<<19))
{
case 0x0://DMA 存储器0地址被占用,将1中数据搬移
for(i=0;i<ADC_BUF_LEN;i++)
{
rs485buf[2*i+41] = ADC_DMARX_BUF1>>8;//高8位
rs485buf[2*i+42] = ADC_DMARX_BUF1&0xFF;//低8位
}
if(USART3_RX_STA&0x8000)
{
len=USART3_RX_STA&0x3fff;//得到此次接收到的数据长度
for(i=0;i<len;i++)
rs485buf=USART3_RX_BUF; //接收缓冲区
USART3_RX_STA=0; //复位接收状态
}
ADC_BUF_OK=1;
break;
case 0x80000://DMA 存储器1地址被占用,将0中数据搬移
for(i=0;i<ADC_BUF_LEN;i++)
{
rs485buf[2*i+41] = ADC_DMARX_BUF0>>8;//高8位
rs485buf[2*i+42] = ADC_DMARX_BUF0&0xFF;//低8位
}
if(USART3_RX_STA&0x8000)
{
len=USART3_RX_STA&0x3fff;//得到此次接收到的数据长度
for(i=0;i<len;i++)
rs485buf=USART3_RX_BUF; //接收缓冲区
USART3_RX_STA=0; //复位接收状态
}
ADC_BUF_OK=1;
break;
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
OSIntExit();
#endif
}
ADC1配置:
#include "adc.h"
#include "delay.h"
//////////////////////////////////////////////////////////////////////////////////
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//ADC 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/7/13
//版本:V1.1
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//20160713 V1.1
//1,修改Adc_Init函数,支持内部温度传感器数据采集.
//2,新增Get_Temprate函数,用于获取内部温度传感器采集到的温度值
//////////////////////////////////////////////////////////////////////////////////
//初始化ADC
//这里我们仅以规则通道为例
//我们开启了ADC1_CH5和内部温度传感器通道(CH18)
void Adc_DMA_Init(void)
{
//先初始化IO口
RCC->APB2ENR|=1<<8; //使能ADC1时钟
RCC->AHB1ENR|=1<<0; //使能PORTA时钟
GPIO_Set(GPIOA,PIN5,GPIO_MODE_AIN,0,0,GPIO_PUPD_PU); //PA5,模拟输入,下拉
RCC->APB2RSTR|=1<<8; //ADCs复位
RCC->APB2RSTR&=~(1<<8); //复位结束
ADC->CCR=2<<16; //ADCCLK=PCLK2/6=6.75/6=1.125Mhz,ADC时钟最好不要超过36Mhz
ADC->CCR|=1<<23; //使能内部温度传感器
ADC1->CR1=0; //CR1设置清零
ADC1->CR2=0; //CR2设置清零
ADC1->CR1|=0<<24; //12位模式
ADC1->CR1|=0<<8; //非扫描模式
ADC1->CR2|=(1<<1); //连续转换模式
ADC1->CR2&=~(1<<11); //右对齐
ADC1->CR2|=0<<28; //软件触发
ADC1->CR2|=1<<9; //DMA禁止选择,只要发生数据转换且DMA=1,便会发出DMA请求。
ADC1->SQR1&=~(0XF<<20);
ADC1->SQR1|=0<<20; //1个转换在规则序列中 也就是只转换规则序列1
//设置通道5的采样时间
ADC1->SMPR2&=~(7<<(3*5)); //通道5采样时间清空
ADC1->SMPR2|=7<<(3*5); //通道5 480个周期,提高采样时间可以提高精确度
ADC1->SMPR1&=~(7<<(3*(18-10))); //清除通道18原来的设置
ADC1->SMPR1|=7<<(3*(18-10)); //通道18 480周期,提高采样时间可以提高精确度,采样率2.34375kHz
ADC1->CR2|=1<<0; //开启AD转换器
ADC1->CR2|=1<<8; //DMA使能
}
//获得ADC值
//ch:通道值 0~18
//返回值:转换结果
void Get_DMA_Adc(u8 ch)
{
//设置转换序列
ADC1->SQR3&=0XFFFFFFE0;//规则序列1 通道ch
ADC1->SQR3|=ch;
ADC1->CR2|=1<<30; //启动规则转换通道
//while(!(ADC1->SR&1<<1));//等待转换结束
//return ADC1->DR; //返回adc值
}
u16 Get_Adc(u8 ch)
{
//设置转换序列
ADC1->SQR3&=0XFFFFFFE0;//规则序列1 通道ch
ADC1->SQR3|=ch;
ADC1->CR2|=1<<30; //启动规则转换通道
while(!(ADC1->SR&1<<1));//等待转换结束
return ADC1->DR; //返回adc值
}
//获取通道ch的转换值,取times次,然后平均
//ch:通道编号
//times:获取次数
//返回值:通道ch的times次转换结果平均值
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
//得到温度值
//返回值:温度值(扩大了100倍,单位:℃.)
short Get_Temprate(void)
{
u32 adcx;
short result;
double temperate;
adcx=Get_Adc_Average(ADC_CH_TEMP,20); //读取通道18,20次取平均
temperate=(float)adcx*(3.3/4096); //电压值
temperate=(temperate-0.76)/0.0025+25; //转换为温度值
result=temperate*=100; //扩大100倍.
return result;
}
一周热门 更多>