M-Arch(番外8)GD32L233评测-ADC和DMA(官方没有例程哦)

前言

开工第一天,今天是DMA方式的ADC采样。

什么是ADC?

ADC即模拟数字转换器,ADC的精度一般用位来表示,位数越多,表示相同模拟量范围内的采样点数越多,那么相应的精度就越高。

比如:12位表示的范围是0 ~ 2^12 (4096),8位表示的范围是0 ~ 2^8 (256),前者的精度是后者的16倍。

ADC一般需要配置的内容包括:

  1. IO配置(时钟,模拟输入)

  2. ADC参数配置(模式-扫描模式,连续模式;触发方式;通道配置-规则组or注入组;ADC校正)

  3. 中断和DMA(使能)配置

【注】ADC的规则组和注入组的区别可简单理解为:规则组是周期执行的程序,注入组是中断程序。

217805a45bedca5d1d9ea349067b42e7.png

什么是DMA?

DMA即直接存储器访问控制器,DMA提供了一种硬件的方式在外设和存储器之间或者存储器和存储器之间传输数据,而无需CPU的介入,避免了CPU多次进入中断进行大规模的数据拷贝,最终提高整体的系统性能。

简单而言,DMA相当于是外请(DMA硬件)的搬运工(数据拷贝),节约宝贵的CPU资源。

DMA一般需要配置的内容包括:

  1. IO配置(时钟)

  2. DMA参数配置(拷贝的方向,内容,地址,通道,模式和数量等)

  3. 中断(使能)配置

DMA的配置需要注意通道的匹配:

f8a0467a7868e8e3ddadf0ca4247bf8c.png
STM32F103 DMA通道配置
00acf9456332d012bca0cf82b90d5634.png
GD32F450 DMA通道配置

注意,在GD32L233中新增了一个DMAMUX的设计,即在DMA控制器前一级增加了的多路选择器,相比如上图,它的好处是更灵活,缺点当然也是更灵活了。

eb0ee8fea577372a34724d0ca944b75e.png
GD32L233 DMAMUX结构

从设计的角度来看,这种配置个人觉得固定成一张表更好理解和处理。

ADC如何DMA?

基本说明

先把DMA的概念代入,ADC属于外设,我们一般使用ADC的DMA是把ADC的采样数据传送到内存中供程序使用,那么它的处理数据链是:ADC通道数据地址-DMA缓存数据-滤波-业务层数据,DMA的确是省了很多软件的事

架构和流程

基本架构:

ceb0a9e81c21fc1bf578878ca1f44260.png

流程和数据结构:

854a0fd52df5d2b89eb2213d0ae0d4d2.png

代码设计

由于单片机内置了CPU温度值,Vrefint和Vbatt(STM32无Vbatt)的采样通道,这里的示例用这几个通道来做示例。

GD32L233的温度计算公式跟GD32F450不一样,在用户手册中写清楚了:

温度(°C) = ((Dtemperature – D30) / Avg_Slope) + 30

D30在芯片内固定:

#define ADC_TEMP_CALIBRATION_VALUE        REG16(0x1FFFF7F8)
adc和dma

adc配置:

void adc1_init(int32_t channel_num)
{
    /* enable ADC clock */
    rcu_periph_clock_enable(RCU_ADC);    
    rcu_periph_clock_enable(RCU_GPIOA);

    gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);
    /* config ADC clock */
    rcu_adc_clock_config(RCU_ADCCK_APB2_DIV16);
    /* ADC SCAN function enable */
    adc_special_function_config(ADC_SCAN_MODE, ENABLE);
    /* conyinuous mode */
    adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE);
    adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);
    adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);
    adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
    adc_channel_length_config(ADC_REGULAR_CHANNEL, channel_num);

    /* ADC temperature and Vrefint enable */
    adc_channel_16_to_19(ADC_TEMP_CHANNEL_SWITCH, ENABLE);
    adc_channel_16_to_19(ADC_INTERNAL_CHANNEL_SWITCH, ENABLE);
    adc_channel_16_to_19(ADC_VBAT_CHANNEL_SWITCH, ENABLE);

    adc_regular_channel_config(0, ADC_CHANNEL_adc_test,   ADC_SAMPLETIME_239POINT5);
    adc_regular_channel_config(1, ADC_CHANNEL_cpu_temper, ADC_SAMPLETIME_239POINT5);
    adc_regular_channel_config(2, ADC_CHANNEL_cpu_vref,   ADC_SAMPLETIME_239POINT5);
    adc_regular_channel_config(3, ADC_CHANNEL_vbatt,      ADC_SAMPLETIME_239POINT5);

    /* enable ADC interface */
    adc_enable();
    adc_dma_mode_enable();
    delay_ms(1U);

    /* ADC calibration and reset calibration */
    adc_calibration_enable();
    adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
}

DMA配置:

void adc1_dma_init(uint32_t addr, uint32_t number)
{
    /* ADC_DMA_channel configuration */
    dma_parameter_struct dma_parameter;

    /* enable DMA0 clock */
    rcu_periph_clock_enable(RCU_DMA);

    /* ADC DMA_channel configuration */
    dma_deinit(DMA_CH0);

    /* initialize DMA data mode */
    dma_struct_para_init(&dma_parameter);
    dma_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_parameter.periph_addr = (uint32_t)(&ADC_RDATA);
    dma_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_parameter.periph_width = DMA_PERIPHERAL_WIDTH_32BIT;
    dma_parameter.memory_addr = addr;
    dma_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT;
    dma_parameter.number = number;
    dma_parameter.priority = DMA_PRIORITY_HIGH;
    dma_parameter.request = DMA_REQUEST_ADC;
    dma_init(DMA_CH0, &dma_parameter);
    dma_circulation_enable(DMA_CH0);

    dma_interrupt_enable(DMA_CH0, DMA_CHXCTL_FTFIE);
    nvic_irq_enable(DMA_Channel0_IRQn, 2);
    
    dma_channel_enable(DMA_CH0);
}
业务代码

头文件:

#define COFF_33_4096_10000000x          8057    ///< 3.3/4096 (10000000)

#if defined(GD32M0)

#define COFF_VBATT  3
#define CPU_TEMP_AD_BASE_1000x          30000   ///< 30度
#define ADC_TEMP_CALIBRATION_VALUE        REG16(0x1FFFF7F8)

#define GET_CPU_TEMPERATURE(data) CPU_TEMP_AD_BASE_1000x + (data - (int32_t)ADC_TEMP_CALIBRATION_VALUE) * COFF_1_4096_10000000x / 10
#define GET_CPU_VREF(data) COFF_33_4096_10000000x*data/10000
#define GET_CPU_VBATT(data) COFF_33_4096_10000000x*data*COFF_VBATT/10000

#define ADC_CHANNEL_adc_test    ((uint8_t)ADC_CHANNEL_1)
#define ADC_CHANNEL_cpu_temper  ((uint8_t)ADC_CHANNEL_16)
#define ADC_CHANNEL_cpu_vref    ((uint8_t)ADC_CHANNEL_17)
#define ADC_CHANNEL_vbatt       ((uint8_t)ADC_CHANNEL_18)

#endif

DMA中断函数:

测试结果

数据扩大1000倍,其中2~4依次是温度,参考电压和电池电压。

adc = [1143] [23654] [1198] [3304]
adc = [1144] [22677] [1198] [3304]
adc = [1144] [22922] [1198] [3304]
adc = [1144] [23410] [1198] [3304]
adc = [1144] [23654] [1198] [3304]
adc = [1144] [22922] [1198] [3304]
c01001cb6e181db6a1eb5f16d138275c.gif ecaff2f4be1bc89acc6b6a9b2a5c740b.gif 66f0fb4227a1e40e6b2adacd9cbc128a.png 0954cd2f3626b023a970b268fabc6bbe.gif
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值