M-Arch(5)第四个示例:ADC&DMA

前言

回顾下之前的章节:

  • 第一章节中我们描述了整个框架的核心设计思路以及主要的文件架构

  • 第二章节中我们基于一个简单的定时器OS实现了串口的数据打印,并完成了通用crc模块的设计和测试

  • 第三章节中我们给出了真随机数和伪随机数的概念和代码示例,并在架构上对接口进行了重构

  • 第四章节中我们回顾了FMC的基本知识,并给出了示例,后面我们将在设计IAP的时候再次使用到FMC

本文我们将总结下ADC和DMA的基本使用方法,并给出示例,从中我们可以看到GD和STM在设计上的差别。

什么是ADC?

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

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

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

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

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

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

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

5daee81e1a2709d0ae45afa77aa9df4c.png

什么是DMA?

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

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

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

  1. IO配置(时钟)

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

  3. 中断(使能)配置

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

50120c07c099f32e8d75a274c1d53ad9.png e4fd61a257ca0c1cb3d09651a44d259c.png

ADC如何DMA?

基本说明

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

架构和流程

基本架构:

27473628959479d130b0da59752ba8ce.png

流程和数据结构:

6a3ecc06f1ed1267cea433e7603fdbdb.png

代码设计

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

adc和dma

头文件:

#ifndef __IO_ADC_H__
#define __IO_ADC_H__

#include <stdint.h>

#ifdef GD32

#define COFF_V25_142_SLOPE_435_100x     32644   ///< 1.42V/4.35mv/度 (100x) gd
#define COFF_V25_142_SLOPE_435_1000x    326437  ///< 1.42V/4.35mv/度 (1000x) gd
#define COFF_VDATA_SLOPE_435_10000x     1852    ///< 3.3/4096/4.35mv/度 (10000x) gd

#define COFF_V25_SLOPE_1000x        COFF_V25_142_SLOPE_435_1000x
#define COFF_VDATA_SLOPE_10000x     COFF_VDATA_SLOPE_435_10000x

#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

#ifdef STM32

#define COFF_V25_143_SLOPE_430_100x     33256   ///< 1.43V/4.3mv/度 (100x) stm
#define COFF_V25_143_SLOPE_430_1000x    332558  ///< 1.43V/4.3mv/度 (1000x) stm
#define COFF_VDATA_SLOPE_430_10000x     1874    ///< 3.3/4096/4.3mv/度 (10000x) stm

#define COFF_V25_SLOPE_1000x        COFF_V25_143_SLOPE_430_1000x
#define COFF_VDATA_SLOPE_10000x     COFF_VDATA_SLOPE_430_10000x

#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_2)

#endif

/* 
    温度 计算公式:
    = 25 + (V25 - data * 3.3 / 4096) / Slope
    = 25 + V25 / Slope - data * 3.3 / 4096 / Slope
    = 25 + COFF_V25_SLOPE - data * COFF_VDATA_SLOPE
*/
#define COFF_33_4096_10000000x          8057    ///< 3.3/4096 (10000000)
#define CPU_TEMP_AD_BASE_1000x          25000   ///< 25度

void adc1_init(int32_t channel_num);
void adc1_dma_init(uint32_t addr, uint32_t number);
void process_adc1_dma(void);


#endif /* __IO_ADC_H__ */

GD32源码:

void adc1_init(int32_t channel_num)
{
    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_ADC0);

    gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);
    adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);
    adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE);
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, EXTERNAL_TRIGGER_DISABLE);
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);
    adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, channel_num);
    
    /* ADC Vbat channel enable */
    adc_channel_16_to_18(ADC_VBAT_CHANNEL_SWITCH, ENABLE);
    /* ADC temperature and Vrefint enable */
    adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH, ENABLE);    
    
    adc_clock_config(ADC_ADCCK_PCLK2_DIV4);
    adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_adc_test,   ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_cpu_temper, ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_cpu_vref,   ADC_SAMPLETIME_144);
    adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_vbatt,      ADC_SAMPLETIME_144);

    /* enable ADC interface */
    adc_enable(ADC0);
    adc_dma_mode_enable(ADC0);
    adc_dma_request_after_last_enable(ADC0);
    

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

void adc1_dma_init(uint32_t addr, uint32_t number)
{
    /* ADC_DMA_channel configuration */
    dma_single_data_parameter_struct dma_data_parameter;
    
    /* enable DMA0 clock */
    rcu_periph_clock_enable(RCU_DMA1);
    
    /* ADC DMA_channel configuration */
    dma_deinit(DMA1, DMA_CH0);
   
    /* initialize DMA data mode */
    dma_single_data_para_struct_init(&dma_data_parameter);
    dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0));
    dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_data_parameter.memory0_addr = addr;
    dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_data_parameter.periph_memory_width = DMA_PERIPH_WIDTH_32BIT;
    dma_data_parameter.circular_mode = DMA_CIRCULAR_MODE_ENABLE;
    dma_data_parameter.direction = DMA_PERIPH_TO_MEMORY;
    dma_data_parameter.number = number;
    dma_data_parameter.priority = DMA_PRIORITY_HIGH;
    dma_single_data_mode_init(DMA1, DMA_CH0, &dma_data_parameter);
    
    dma_channel_subperipheral_select(DMA1, DMA_CH0, DMA_SUBPERI0);
    
    dma_interrupt_enable(DMA1, DMA_CH0, DMA_CHXCTL_FTFIE);
    nvic_irq_enable(DMA1_Channel0_IRQn, 1, 1);
    
    dma_channel_enable(DMA1, DMA_CH0);
}

STM32源码:

void adc1_init(int32_t channel_num)
{
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_DeInit(ADC1);
    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    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_InitStructure.ADC_NbrOfChannel = channel_num;
    ADC_Init(ADC1, &ADC_InitStructure);
    
    /* ADC temperature and Vrefint enable */
    ADC_TempSensorVrefintCmd(ENABLE);
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL_adc_test, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL_cpu_temper, 2, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL_cpu_vref, 3, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL_vbatt, 4, ADC_SampleTime_55Cycles5);

    /* enable ADC interface */
    ADC_Cmd(ADC1, ENABLE);
    ADC_DMACmd(ADC1, ENABLE);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);    

    /* ADC calibration and reset calibration */
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));

}

void adc1_dma_init(uint32_t addr, uint32_t number)
{
    DMA_InitTypeDef DMA_InitStructure;
 
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 DMA_DeInit(DMA1_Channel1);

 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&ADC1->DR);
 DMA_InitStructure.DMA_MemoryBaseAddr = addr;
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
 DMA_InitStructure.DMA_BufferSize = number;
 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
 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);
    
    DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
    nvic_irq_enable(DMA1_Channel1_IRQn, 1, 1);
}

业务代码

system_data_t g_system_data;

#define ADC_SAMPLE_FILTER_COUNT 10  ///< adc滤波次数


typedef struct sample_interface
{
    uint32_t channel;                   // 通道
    void *result;                       // 结果
    int32_t (*process_func)(int32_t);   // 处理函数
}sample_interface_t;

static int32_t get_common_data(int32_t data);
static int32_t get_cpu_temperature_common_1000x(int32_t coff_v25_slope_1000x, int32_t coff_vdata_slope_10000x, int32_t data);
static int32_t get_cpu_temperature_1000x(int32_t data);
static int32_t get_cpu_vref_1000x(int32_t data);
static int32_t get_vbatt_1000x(int32_t data);

sample_interface_t sample_interfaces[] = {
    { ADC_CHANNEL_adc_test,     &g_system_data.adc_test,    get_common_data },
    { ADC_CHANNEL_cpu_temper,   &g_system_data.cpu_temper,  get_cpu_temperature_1000x },
    { ADC_CHANNEL_cpu_vref,     &g_system_data.cpu_vref,    get_cpu_vref_1000x },
    { ADC_CHANNEL_vbatt,        &g_system_data.vbatt,       get_vbatt_1000x },
};
#define ADC_CHANNEL_NUM (sizeof(sample_interfaces)/sizeof(sample_interfaces[0]))

uint32_t adc_samples[ADC_CHANNEL_NUM*ADC_SAMPLE_FILTER_COUNT];
uint32_t adc_values[ADC_CHANNEL_NUM];

void sample_init(void)
{
    adc1_dma_init((uint32_t)&adc_samples, ADC_CHANNEL_NUM*ADC_SAMPLE_FILTER_COUNT);
    adc1_init(ADC_CHANNEL_NUM);
}

void process_adc1_dma(void)
{
    int index;
    uint32_t tmp = 0;
    memset(adc_values, 0x00, sizeof(adc_values));
    for (index = 0; index < ADC_CHANNEL_NUM*ADC_SAMPLE_FILTER_COUNT; index++)
    {
        adc_values[index%ADC_CHANNEL_NUM] += adc_samples[index];
    }
    for (index = 0; index < ADC_CHANNEL_NUM; index++)
    {
        tmp = adc_values[index] / ADC_SAMPLE_FILTER_COUNT;
        adc_values[index] = tmp;
        *(int32_t *)sample_interfaces[index].result = sample_interfaces[index].process_func(tmp);
    }
}

static int32_t get_common_data(int32_t data)
{
    return data;
}

static int32_t get_cpu_temperature_1000x(int32_t data)
{
    return get_cpu_temperature_common_1000x(COFF_V25_SLOPE_1000x, COFF_VDATA_SLOPE_10000x, data);
}

static int32_t get_cpu_vref_1000x(int32_t data)
{
    return (int32_t)COFF_33_4096_10000000x*data/10000;
}

static int32_t get_vbatt_1000x(int32_t data)
{
    return (int32_t)COFF_33_4096_10000000x*data*4/10000;
}

static int32_t get_cpu_temperature_common_1000x(int32_t coff_v25_slope_1000x, int32_t coff_vdata_slope_10000x, int32_t data)
{
    return CPU_TEMP_AD_BASE_1000x + coff_v25_slope_1000x - data * coff_vdata_slope_10000x / 10;
}

例行给出运行结果

STM32F1-COM3,GD32F4-COM9

CPU温度参考值:29.234℃(STM32F1),29.004℃( GD32F4)

Vrefint值:1.219V(STM32F1),1.198V(GD32F4)

Vbatt值:3.280V(GD32F4)

85b1615a704bd1351823b4fcd28af448.gif

--EOF--

例行求粉,谢谢!

a95a0238ec4468d400e1d1228229c527.png
求粉
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值