1. ADC模式介绍
扫描模式: 若ADC配置为多通道,配置为扫描模式。若ADC配置为单通道,配置为不扫描模式。这个模式就是自动扫描你开启的所有通道进行转换,直至转换完。例如你开启了CH0、CH1、CH2、CH3这四个通道,启动转换后ADC会自动将这4个通道全部转换完。
连续模式:若ADC自动连续转换,就配置连续模式。若ADC每开启一次,就转换一次,就是单次模式。开启连续模式后,ADC的转换不由其他控制。例如将ADC设置为了定时器的TGRO触发采样,如果开启连续模式,ADC将忽略定时器的触发采样。(连续转换模式开启后其实就是满频率的采样)。
间断模式: 可以将多个通道进行分组采集,例如你开启了CH0~7这8个通道,假如你设置了间断次数为1,就相当于将8个通道分成了8组,每组1个通道,那么要想采集完这8个通道就需要手动触发8次ADC采集;如果设置了间断次数为4,那么采集完8个通道就需要手动触发4次ADC采集。
触发方式:分为外部事件触发和软件触发,这里一般选用软件触发。就是由软件来开启ADC转换。
独立模式:只使用一个ADC就使用独立模式。
数据对齐方式:就是采样值以哪种对齐方式存储在寄存器中。
2. DMA相关概念:
DMA模式:分为正常模式和循环模式。正常模式就是每开启一次传输,就传输一组数据。
循环模式:只用开启一次DMA传输就可以了。
DMA传输方向:分为外设->内存,内存->外设,内存->内存三种。
DMA传输长度:每次DMA传输的数据长度。
外设传输单位:以字节或半字或字作为1个单位(1个数据长度)。
内存传输单位:以字节或半字或字作为1个单位(1个数据长度)。
外设地址自增:每次DMA传输时外设地址是否自动加1。
内存地址自增:每次DMA传输时内存地址是否自动加1。
DMA优先级:DMA1有8个通道,每个通道都有自己的优先级,当同时有N个通道发送DMA传输请求时,优先级高的先进行DMA传输。
3. ADC多通道采集方案
方案一:不使用DMA方式+扫描模式+间断模式+不使用连续模式+独立模式
代码如下:
第1步:初始化ADC管脚
//1.使能时钟
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
//2.ADC管脚配置
GPIO_InitStruct.Pin = LL_GPIO_PIN_0|LL_GPIO_PIN_1|LL_GPIO_PIN_2|LL_GPIO_PIN_3
|LL_GPIO_PIN_4|LL_GPIO_PIN_5|LL_GPIO_PIN_6|LL_GPIO_PIN_7;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
//3.进行校验
LL_ADC_Enable(ADC1); //使能ADC
LL_ADC_StartCalibration(ADC1); //开始校准
while (LL_ADC_IsCalibrationOnGoing(ADC1)); //等待校准完成
注意,这里初始化ADC后要进行1次校准,且校准在配置规则通道之前,否则可能会出现采集通道的数据错乱。
第2步:ADC模式配置
//1.禁止ADC
LL_ADC_Disable(ADC1);
//2.配置ADC模式
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT; //数据右对齐
ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_ENABLE; //扫描模式
LL_ADC_Init(ADC1, &ADC_InitStruct);
ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT; //独立模式
LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; //软件触发ADC转换
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_8RANKS; //规则通道数量8个
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_1RANK;//间断模式,每个间断读取1个通道
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE; //单次触发
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE; //不使用DMA
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
注意,在初始化ADC模式时要禁止ADC,否则下面的配置全部不成功,这里的坑花了我1天时间来查问题。
第3步:规则通道配置
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_0);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_0, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_2);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_2, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_3);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_5, LL_ADC_CHANNEL_4);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_6, LL_ADC_CHANNEL_5);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_5, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_7, LL_ADC_CHANNEL_6);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_6, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_8, LL_ADC_CHANNEL_7);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_7, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_Enable(ADC1); //使能ADC
这里就是添加8个规则通道的读取顺序并使能ADC。将ADC1通道0,通道1,通道2,通道3,通道4,通道5,通道6,通道7分别设置到规则通道的1,2,3,4,5,6,7,8上。这样每次ADC转换时,会按照ADC1通道0~通道7的顺序依次转换。至此,ADC初始化完毕。
第4步 顺序读取ADC值
//1.判断ADC是否使能
if (LL_ADC_IsEnabled(ADC1) != ADC_DMA_STATE_READY)
{
return FALSE;
}
//2.清除ADC1转换完成标记
LL_ADC_ClearFlag_EOS(ADC1);
//3.循环开启通道转换并获取采样值
for ( i = 0; i < length; i++)
{
LL_ADC_REG_StartConversionSWStart(ADC1);
delay_ms(1);
pdata[i] = LL_ADC_REG_ReadConversionData12(ADC1);
}
方案二:使用DMA单次传输方式+扫描模式+不使用间断模式+不使用连续模式+独立模式
第1步:DMA初始化
//DMA1时钟使能
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
//DMA1配置
LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_1, LL_DMA_DIRECTION_PERIPH_TO_MEMORY); //传输方向:外设->内存
LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PRIORITY_HIGH); //优先级高
LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MODE_NORMAL); //正常模式
LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PERIPH_NOINCREMENT); //外设地址不自增
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MEMORY_INCREMENT); //内存地址自增
LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_PDATAALIGN_HALFWORD); //外设传输以半字为单位
LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_1, LL_DMA_MDATAALIGN_HALFWORD); //内存接收以板子为单位
这里没有设置DMA中断,会在ADC初始化完再进行配置。
DMA传输的外设地址,内存地址,传输长度这里没有进行配置,在每次开启DMA传输时在进行相关的配置。这样可以更灵活的进行DMA传输。
第2步:初始化ADC管脚
跟方案一第1步处理一样。
第3步:ADC模式配置
//1.禁止ADC
LL_ADC_Disable(ADC1);
//2. 配置ADC模式
ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT; //右对齐
ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_ENABLE; //扫描模式
LL_ADC_Init(ADC1, &ADC_InitStruct);
ADC_CommonInitStruct.Multimode = LL_ADC_MULTI_INDEPENDENT; //独立模式
LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_SOFTWARE; //软件触发
ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_ENABLE_8RANKS; //规则通道数8个
ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE; //间断模式禁止
ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE; //单次触发
ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_NONE; //不使用DMA
LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
和方案一相比,这里禁止间断模式。注意,这里设置为不使用DMA,是防止意外的ADC转换导致通道数据错乱,进而使DMA传输的数据不能和对应通道数据匹配,所以先禁止DMA传输。每次开启ADC转换时,再配置为使用DMA。
第4步:规则通道配置
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_0);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_0, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_2, LL_ADC_CHANNEL_1);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_1, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_3, LL_ADC_CHANNEL_2);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_2, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_4, LL_ADC_CHANNEL_3);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_3, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_5, LL_ADC_CHANNEL_4);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_4, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_6, LL_ADC_CHANNEL_5);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_5, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_7, LL_ADC_CHANNEL_6);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_6, LL_ADC_SAMPLINGTIME_239CYCLES_5);
LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_8, LL_ADC_CHANNEL_7);
LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_7, LL_ADC_SAMPLINGTIME_239CYCLES_5);
将ADC1通道0,通道1,通道2,通道3,通道4,通道5,通道6,通道7分别设置到规则通道的1,2,3,4,5,6,7,8上。这样每次ADC转换时,会按照ADC1通道0~通道7的顺序依次转换。
第5步:设置DMA中断
NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),7, 0));
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
LL_ADC_Enable(ADC1); //使能ADC
设置DMA传输完成中断,设置优先级7。最后使能ADC。
第6步:编写DMA传输完成中断处理函数
void DMA1_Channel1_IRQHandler(void)
{
if (LL_DMA_IsActiveFlag_TC1(DMA1) != 0) {
LL_DMA_ClearFlag_TC1(DMA1);
g_adc_dma_state = ADC_DMA_STATE_READY;
}
}
(1)判断中断标记,是否是DMA传输完成导致的中断;
(2)清除中断标记;
(3)设置标记,表明产生了DMA传输完成中断。
第7步:开启ADC转换和DMA传输
static uint8_t my_adc_start_dma(uint16_t *pdata, uint32_t length)
{
if (g_adc_dma_state == 0)
{
if (LL_ADC_IsEnabled(ADC1) != ADC_DMA_STATE_READY)
{
return FALSE;
}
//1.清除ADC转换完成标记
LL_ADC_ClearFlag_EOS(ADC1);
//2.清除DMA传输完成标记并禁止DMA传输
LL_DMA_ClearFlag_GI1(DMA1);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
//3.配置DMA传输长度,外设地址,内存地址
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, length);
LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_1, (uint32_t)&(ADC1->DR),
(uint32_t)pdata, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
//4.使能DMA传输完成中断
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
//5.配置ADC1使用DMA
LL_ADC_REG_SetDMATransfer(ADC1, LL_ADC_REG_DMA_TRANSFER_UNLIMITED);
//6.使能DMA传输
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
//7.软件触发ADC1转换
LL_ADC_REG_StartConversionSWStart(ADC1);
}
return TRUE;
}
这样,ADC转换完成,DMA传输完成后,pdata数组内就是ADC1转换的AD采样值。且每开启一次ADC转换,就通过DMA传输一组数据。
1167





