完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
参考资料:
STM32F10x系列芯片ADC通道和引脚对应关系。 stm32之adc多路采集+dma传输 8路ADC adc.h #ifndef __ADC_H #define __ADC_H #include "sys.h" #define N 100 //每通道采100次,再求平均 #define M 8 //为8个通道 extern u16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址 extern u16 Average[M]; //用来存放求平均值之后的结果 void Adc_Init(void); #endif adc.c #include "adc.h" #include "delay.h" u16 AD_Value[N][M]; //用来存放ADC转换结果,也是DMA的目标地址,每完成一次转换,DMA将数据依次存放到数组 u16 Average[M]; //用来存放求平均值之后的结果 //初始化ADC //这里我们仅以规则通道为例 void Adc_Init(void) { ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE ); //使能ADC1通道时钟 RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M //PA0/PA1/PA2/PA3/PA4/PA5 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚 GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为默认值 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1独立模式 ADC_InitStructure.ADC_ScanConvMode = ENABLE; //模数转换工作在扫描通道模式 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel =8; //顺序进行规则转换的ADC通道的数目 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_239Cycles5 ); ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 8, ADC_SampleTime_239Cycles5 ); ADC_DMACmd(ADC1, ENABLE); //使能 ADC_DMA ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 ADC_ResetCalibration(ADC1); //使能复位校准 while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束 ADC_StartCalibration(ADC1); //开启AD校准 while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束 ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 } dma.h #ifndef __DMA_H #define __DMA_H #include "sys.h" void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);//配置DMA1_CHx void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);//使能DMA1_CHx #endif dma.c #include "dma.h" DMA_InitTypeDef DMA_InitStructure; //DMA1的各通道配置 //这里的传输形式是固定的,这点要根据不同的情况来修改 //从存储器->外设模式/16位数据宽度/存储器增量模式 //DMA_CHx:DMA通道CHx //cpar:外设地址 //cmar:存储器地址 //cndtr:数据传输量 void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr) { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输 DMA_DeInit(DMA_CHx); //将DMA的通道1寄存器重设为缺省值 DMA_InitStructure.DMA_PeripheralBaseAddr = cpar; //DMA外设ADC基地址 DMA_InitStructure.DMA_MemoryBaseAddr = cmar; //DMA内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设发送到内存 DMA_InitStructure.DMA_BufferSize = cndtr; //DMA通道的DMA缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //数据宽度为16位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式 DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA通道 x拥有中优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输 DMA_Init(DMA_CHx, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); //启动DMA通道 } main.c #include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "lcd.h" #include "adc.h" #include "dma.h" int main(void) { int i=0,j=0; float temp,SUM; delay_init(); //延时函数初始化 uart_init(9600); //串口初始化为9600 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); //初始化LCD Adc_Init(); //ADC初始化 MYDMA_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)&AD_Value,N*M);//DMA1通道1,外设为ADC1,存储器地址&AD_Value,长N*M POINT_COLOR=RED;//设置字体为红色 LCD_ShowString(20,15,200,24,24,"8 Channel ADC+DMA"); POINT_COLOR=MAGENTA;//设置字体为红色 LCD_ShowString(60,40,200,16,16,"ADC_CH0_VOL:0.000V"); LCD_ShowString(60,60,200,16,16,"ADC_CH1_VOL:0.000V"); LCD_ShowString(60,80,200,16,16,"ADC_CH2_VOL:0.000V"); LCD_ShowString(60,100,200,16,16,"ADC_CH3_VOL:0.000V"); LCD_ShowString(60,120,200,16,16,"ADC_CH4_VOL:0.000V"); LCD_ShowString(60,140,200,16,16,"ADC_CH5_VOL:0.000V"); LCD_ShowString(60,160,200,16,16,"ADC_CH6_VOL:0.000V"); LCD_ShowString(60,180,200,16,16,"ADC_CH7_VOL:0.000V"); LCD_ShowString(80,200,200,24,24,"ADC+DMA"); while(1) { //while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET ); for(j=0;j for(i=0;i SUM+=AD_Value[j]; //每个通道的数据求和,注意是二维数组的每列的数据求和 } Average[j]=SUM/N; //再取平均 temp=(float)Average[j]*(3.3/4096.0); Average[j]=temp; LCD_ShowxNum(156,40+20*j,Average[j],1,16,0);//显示电压值的整数部分 temp-=Average[j]; temp*=1000; LCD_ShowxNum(172,40+20*j,temp,3,16,0X80);//显示电压值的小数部分 Average[j]=0;//开众提醒置零 SUM=0;//开众提醒置零 } POINT_COLOR=BLACK;//设置字体为黑色 show_sentence24_24(20,260,200,24,24,"棒棒您好"); show_sentence24_24(100,290,200,24,24,"恭喜成功了"); LED0=!LED0; delay_ms(250); } } 把前面main.c的封装改造一下,其他文件不变: #include "led.h" #include "delay.h" #include "sys.h" #include "usart.h" #include "lcd.h" #include "adc.h" #include "dma.h" void Show_other(void); void ShowCH_Voltage(void); int main(void) { delay_init(); //延时函数初始化 uart_init(9600); //串口初始化为9600 LED_Init(); //初始化与LED连接的硬件接口 LCD_Init(); //初始化LCD Adc_Init(); //ADC初始化 MYDMA_Config(DMA1_Channel1,(u32)&ADC1->DR,(u32)&AD_Value,N*M);//DMA1通道1,外设为ADC1,存储器地址&AD_Value,长N*M Show_other(); while(1) { while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET);//每次搬运完,再处理数组得出电压 POINT_COLOR=RED;//设置字体为红色 ShowCH_Voltage(); } } void ShowCH_Voltage(void) { int i=0,j=0; float temp,SUM; for(j=0;j for(i=0;i SUM+=AD_Value[j]; //每个通道的数据求和,注意是二维数组的每列的数据求和 } Average[j]=SUM/N; //再取平均 temp=(float)Average[j]*(3.3/4096.0); Average[j]=temp; LCD_ShowxNum(144,40+30*j,Average[j],1,24,0);//显示电压值的整数部分 temp-=Average[j]; temp*=10000; LCD_ShowxNum(144+24,40+30*j,temp,4,24,0X80);//显示电压值的小数部分 Average[j]=0;//开众提醒置零 SUM=0;//开众提醒置零 } } void Show_other(void) { int j=0; u8 ADC_Ch[20]; POINT_COLOR=MAGENTA;//设置字体为紫色 LCD_ShowString(20,15,200,24,24,"8 Channel ADC+DMA"); show_sentence24_24(20,275,200,24,24,"棒棒恭喜成功"); LCD_ShowString(120,295,200,24,24,"2020/12/4"); for(j=0;j sprintf((char*)ADC_Ch,"ADC_CH%d_VOL:0.0000V",j);//将int变量j打印到ADC_Ch数组。 LCD_ShowString(0,40+30*j,240,24,24,ADC_Ch); } } |
|
|
|
正点原子mini测试图:
有点疑惑:为什么数字会不停的跳变,都没有插线,连接通道5的接地,不是0.0000,也跳变。 总结: 配置ADC和DMA参数很重要, 对于ADC: 通道引脚配置成模拟输入 ,ADC采用扫描通道模式,循环连续转换,每转换一个通道,就将值存到ADC的DR寄存器,就一个这样的数据寄存器,所以定义了一个二维数组,通道DMA将ADC的DR寄存器每次更新的数据保存到二维数组,每扫描一趟,得到的是各通道的值,8个通道就得到8个值(可看成一行),为得到有代表性的值,采集100次,然后取平均值,这样一来就有100行,对于二维数组每一列就是某个通道的值,将每列求和再取平均作为该通道的电压值。 **对于DMA:**配置地址,是将ADC的DR作为外设地址,将存数据的二维数组,作为内存地址,每当有通道转换结束时,就会请求DMA,将DR的数据搬到数组,搬数据永远在DR,但是存数据的是数组,所以外设递增不递增,内存地址递增。ADC循环扫描,所以DMA也循环模式搬运,每搬完一个二维数组大小的量(N*M),就置位TC,没有中断功能,所以没有使能TC中断,只是查看标志:while((DMA_GetFlagStatus(DMA1_FLAG_TC1)) == RESET ); 因为是ADC+DMA:不要忘了开启ADC_DMACmd ADC_DMACmd(ADC1, ENABLE); //使能 ADC_DMA 注意这个for循环,不是和平时输出二维数组那种架构 代码有一定的技巧性,比如 想的到浮点数的整数部分,就将浮点数直接赋值给非浮点数(u8,int,chan等),小数部分就用浮点数减去整数部分。 for(j=0;j for(i=0;i SUM+=AD_Value[j]; //每个通道的数据求和,注意是二维数组的每列的数据求和 } Average[j]=SUM/N; //再取平均 temp=(float)Average[j]*(3.3/4096.0); Average[j]=temp; LCD_ShowxNum(144,40+30*j,Average[j],1,24,0);//显示电压值的整数部分 temp-=Average[j]; temp*=10000; LCD_ShowxNum(144+24,40+30*j,temp,4,24,0X80);//显示电压值的小数部分 Average[j]=0;//开众提醒置零 SUM=0;//开众提醒置零 } 还有sprintf这个函数,对于C这种底层语言来说太好用了,可用来直接更改字符串中需要更改的数字。 for(j=0;j sprintf((char*)ADC_Ch,"ADC_CH%d_VOL:0.0000V",j);将int变量j打印到ADC_Ch数组。 LCD_ShowString(0,40+30*j,240,24,24,ADC_Ch); } 对照一下,和正点原子minifly遥控器的配置一样,注释得很到位 5个通道循环采集,每趟采集的数依次分别存入数组adc_value,采集10趟就需要50个数组元素来存储。ADC的配置为DMA_BufferSize = 5*ADC_SAMPLE_NUM; 因为ADC的DC寄存器是12位,所以定义每个数组元素为16位的。 #define ADC_SAMPLE_NUM 10//每个通道采样次数 u16 adc_value[5*ADC_SAMPLE_NUM];//ADC采集值存放缓冲区 //初始化ADC,使用DMA传输 采用半字节,连续,扫描模式传输 //初始化ADC,使用DMA传输 //通道PA0PA1PA3PA4 void Adc_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; DMA_InitTypeDef DMA_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//使ADC1时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟 //PA012 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚 GPIO_Init(GPIOA, &GPIO_InitStructure); //PB01 作为模拟通道输入引脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStructure); //DMA 配置 DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //ADC1->DR地址 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&adc_value;//内存地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 5*ADC_SAMPLE_NUM; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址增加 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //半字 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环传输 DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_Cmd(DMA1_Channel1, ENABLE); ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值 ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式 ADC_InitStructure.ADC_ScanConvMode = ENABLE; //扫描模式,用于多通道采集 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换模式 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐 ADC_InitStructure.ADC_NbrOfChannel = 5; //顺序进行规则转换的ADC通道的数目 ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器 ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1 ADC_DMACmd(ADC1, ENABLE);//使能ADC1 DMA RCC_ADCCLKConfig(RCC_PCLK2_Div6); //设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M //配置连续转换通道,55.5个采样周期 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5); //1个通道转换一次耗时21us 4个通道 ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); //采样个数ADC_SAMPLE_NUM ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_239Cycles5); //总共耗时4*21*ADC_SAMPLE_NUM(64)=5.4ms<10ms ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 4, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 5, ADC_SampleTime_239Cycles5); ADC_ResetCalibration(ADC1); //使能复位校准 while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束 ADC_StartCalibration(ADC1); //开启AD校准 while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束 ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能 } ADC_Filter函数也是这样求平均的,它采用的是一维数组,每隔5个元素就是同一个通道的数据,分别把5个通道的数据全加起来,求每个通道的平均值。利用指针将结果存入adc_val。adc_val数组的每个元素对应每个通道的均值。 //ADC均值滤波 void ADC_Filter(u16* adc_val)//;;;;未被调用过 { u16 i=0; u32 sum[5]={0,0,0,0}; for(;i sum[0]+=adc_value[5*i+0]; sum[1]+=adc_value[5*i+1]; sum[2]+=adc_value[5*i+2]; sum[3]+=adc_value[5*i+3]; sum[4]+=adc_value[5*i+4]; } adc_val[0]=sum[0]/ADC_SAMPLE_NUM; adc_val[1]=sum[1]/ADC_SAMPLE_NUM; adc_val[2]=sum[2]/ADC_SAMPLE_NUM; adc_val[3]=sum[3]/ADC_SAMPLE_NUM; adc_val[4]=sum[4]/ADC_SAMPLE_NUM; } 通道PA0——————BAT 通道PA1——————ROLL 通道PA2——————PITCH 通道PB0——————YAW 通道PB1——————THRUSH 将说明的通道关系,用宏定义连续起来 #define ADC_BAT 0 #define ADC_ROLL 1 #define ADC_PITCH 2 #define ADC_YAW 3 #define ADC_THRUST 4 如果想获取某个通道的平均值,就将其传给下面函数的参数 u16 getAdcValue(u8 axis)//根据不同参数,获取不同姿态或油门的ADC值 { u32 sum=0; for(u8 i=0;i sum += adc_value[5*i+axis]; } return sum/ADC_SAMPLE_NUM; } PA012和PB01都属于ADC1 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1935浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
728浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
568浏览 3评论
593浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
551浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 15:45 , Processed in 0.867469 second(s), Total 81, Slave 64 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号