分享基于STM32H723的ADC+DMA的配置方法
1、ADC寄存器配置,这里以ADC3为例,使用四个通道,分别是4、8、9以及内部温度传感器17通道;
//先初始化IO口
RCC->AHB4ENR |= 1 << 24; //使能ADC3时钟
RCC->AHB4ENR |= 1 << 5; //使能PORT F时钟
GPIO_Set(GPIOF,PIN6,GPIO_MODE_AIN,0,0,GPIO_PUPD_NONE);//PF6,模拟输入,不带上下拉
GPIO_Set(GPIOF,PIN4,GPIO_MODE_AIN,0,0,GPIO_PUPD_NONE); //PF4,模拟输入,不带上下拉
GPIO_Set(GPIOF,PIN5,GPIO_MODE_AIN,0,0,GPIO_PUPD_NONE); //PF5,模拟输入,不带上下拉//DMA配置这里我用了自己仿以前ST标准库的封装方式
DMA_InitStruct.u32DMA_NumberData = 4; //数量
DMA_InitStruct.u32DMA_Memory1BaseAddr = (uint32_t)ADCValueBuffer;//DMA存储器地址
DMA_InitStruct.u32DMA_PeripheralBaseAddr = (uint32_t)&ADC3->DR;//外设地址
DMA_InitStruct.u8DMA_Channel = 115;
DMA_InitStruct.u8DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStruct.u8DMA_DoubleBufferMode = DMA_DoubleBuffer_Disable;DMA_InitStruct.u8DMA_MemoryBurstTransfer = DMA_MemoryBurst_Single;
DMA_InitStruct.u8DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStruct.u8DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.u8DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.u8DMA_PeripheralBurstTransfer = DMA_PeripheralBurst_Single;
DMA_InitStruct.u8DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStruct.u8DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.u8DMA_Priority = DMA_Priority_VeryHigh;
DMA_Driver_Init(DMA1_Stream2,&DMA_InitStruct);RCC->AHB4RSTR |= 1 << 24 ; //ADC3复位
RCC->AHB4RSTR &= ~(1 << 24); //复位结束
RCC->D3CCIPR &= ~(3 << 16); //ADCSEL[1:0]清零
RCC->D3CCIPR |= 2 << 16;//ADCSEL[1:0]=2,per_ck作为ADC时钟源,默认选择hsi_ker_ck作为per_ck,频率:64Mhz
ADC3_COMMON->CCR |= 2 << 18;//PRESC[3:0]=2,输入时钟4分频,即adc_ker_ck=per_ck/4=64/4=16Mhz(不能超过36Mhz)
ADC3_COMMON->CCR |= 1 << 23;//VSENSEEN = 1,开启内部温度传感器
ADC3->CR = 0;//CR寄存器清零,DEEPPWD清零,从深度睡眠唤醒.
ADC3->CR |= 1 << 28;
//ADVREGEN=1,使能ADC稳压器
/*需要在这里实现延时,我这里延时了50ms等待稳压器启动完成*/
ADC3->CFGR &= ~(1 << 13);
//CONT=0,单次转换模式,使用单次转换是为了ADC每转换完一次,都由软件再次触发采样
ADC3->CFGR |= 1 << 12;//OVRMOD=1,复写模式(DR寄存器可被复写)
ADC3->CFGR &= ~(3 << 10);//EXTEN[1:0]=0,软件触发
ADC3->CFGR &= ~(3 << 3);//RES[1:0]位清零
ADC3->CFGR |= 0 << 3;//RES[1:0]=0,12位分辨率(0,12位;1,10位;2,8位.3,6位)
ADC3->CFGR |= 3 << 0;//DMA循环模式
ADC3->CFGR &= ~(1 << 15);
//数据右对齐.
ADC3->CFGR2 &= ~((uint32_t)15 << 5);//OVSS[3:0]=0,不左移,数据右对齐.
ADC3->CFGR2 &= ~((uint32_t)0x7 << 2);//OSR[2:0]=0,不使用过采样
ADC3->CR &= ~((uint32_t)1 << 30);
//ADCALDIF=0,校准单端转换通道
ADC3->CR |= (uint32_t)1 << 31; //开启校准while(ADC3->CR&((uint32_t)1 << 31)) //等待校准完成
{
/*在这里实现等待校准完成,做个计时,超时即退出并返回错误*/
}
ADC3->SQR1 &= ~(0XF << 0);
//L[3:0]清零
ADC3->SQR1 |= (ADC3_CHANNEL_NUM - 1) << 0; //L[3:0]=3,4个转换在规则序列中 也就是转换规则序列1、2、3、4四个序列ADC3->SQR1 &= ~(0x1F << (6 * 1));
ADC3->SQR1 |= 4 << (6 * 1);//设置序列1的通道号为4
ADC3->SQR1 &= ~(0x1F << (6 * 2));
ADC3->SQR1 |= 17 << (6 * 2);//设置序列2的通道号为17
ADC3->SQR1 &= ~(0x1F << (6 * 3));
ADC3->SQR1 |= 8<< (6 * 3);//设置序列3的通道号为8
ADC3->SQR1 &= ~(0x1F << (6 * 4));
ADC3->SQR1 |= HARDWARE_VERSION_ADC_CHANNEL << (6 * 4);//设置序列4的通道号为9
//设置通道8的采样时间
ADC3->SMPR1 &= ~(7 << (3 * 8));//通道8采样时间清空
ADC3->SMPR1 |= 7 << (3 * 8);//通道8 640.5个周期,提高采样时间可以提高精确度
//设置通道9的采样时间
ADC3->SMPR1 &= ~(7 << (3 * 9));//通道9采样时间清空
ADC3->SMPR1 |= 7 << (3 * 9);//通道9 640.5个周期,提高采样时间可以提高精确度
//设置通道4的采样时间
ADC3->SMPR1 &= ~(7 << (3 * 4));//通道4采样时间清空
ADC3->SMPR1 |= 7 << (3 * 4);//通道4 640.5个周期,提高采样时
//设置通道17的采样时间
ADC3->SMPR2 &= ~(7 << (3 * 7));//通道17采样时间清空
ADC3->SMPR2 |= 7 << (3 * 7);//通道17 640.5个周期,提高采样时间可以提高精确度
DMA1_Stream2->CR |= 1 << 0; /* 开启DMA传输 */
ADC3->CR |= 1 << 0; /* 启动ADC */return 1;
以上便是ADC+DMA的寄存器配置,此时基本的配置已经完成,接下来便是触发采样,需要注意的是DMA配置的是正常模式,ADC配置的是单次转换模式,此时是ADC采样完一个通道的数据,则会搬运一次数据至设置的RAM地址中,ADC设定了需转换四个规则序列,则DMA需要搬运四次,搬运完后,需要再次触发ADC采样,则需要再次触发采样。而如果需要ADC一直循环采样及DMA一直搬运的话,则可以将ADC配置为连续转换模式,DMA配置为循环模式,此时DMA设置的缓存地址才会与ADC预设的转换规则序列相对应上。如果ADC为连续转换,而DMA为正常模式,则会出现AD数值循环在DMA设置的缓存地址中滚动存储的情况;
下面是触发ADC采样的寄存器实现
/*******************************************************************************
** 函数名称:Get_ADC3_Value_From_DMA
** 函数作用:从DMA接收ADC数据
** 输入参数:pBuffer------------接收缓存
** u8Length-----------接收几个通道
** 输出参数:0------------------失败
** 1------------------成功
** 使用样例:无
** 函数备注:无
*******************************************************************************/
uint8_t Get_ADC3_Value_From_DMA(uint16_t *pBuffer,uint8_t u8Length){
static uint8_t u8TimeCounter = 0;
ADC3->CR &= ~(1 << 0); /* 先关闭ADC */
ADC3->SQR1 &= ~(0XF << 0); //L[3:0]清零
ADC3->SQR1 |= (u8Length - 1) << 0;//L[3:0] = u8Length - 1,u8Length - 1个转换在规则序列中 也就是转换规则序列前 u8Length - 1个
DMA1_Stream2->CR &= ~(1 << 0); /* 关闭DMA传输 */
u8TimeCounter = 0;
while (DMA1_Stream2->CR & 0X1) /* 确保DMA可以被设置 */
{
/*在这里实现计时*/
}
DMA1->LIFCR |= 0x3D << 16;
DMA1_Stream2->NDTR = u8Length; /* 要传输的数据项数目 */
DMA1_Stream2->CR |= 1 << 0; /* 开启DMA传输 */
ADC3->CR |= 1 << 0; /* 重新启动ADC */
ADC3->CR |= 1 << 2; /* 启动规则转换通道 */while(1)
{
if(DMA1_Stream2->NDTR == 0)
{
SCB_InvalidateDCache_by_Addr((uint32_t *)g_u16ADCValueBuffer, sizeof(g_u16ADCValueBuffer));/*在这里实现数据拷贝*/
memcpy(pBuffer,g_u16ADCValueBuffer,(u8Length * 2));
return 1;
}
else
{
if(++u8TimeCounter > 100)
{
u8TimeCounter = 0;
return 0;
}
}
}}
以上便是ADC+DMA的寄存器配置及获取数据的实现。此时DMA的BUFFER中,0则是规则序列1的采样值,1则是规则序列2中的通道的采样值,一次类推。