STM32F103 基于ll库的ADC多通道采集

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传输一组数据。

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值