资讯详情

GD32450i-EVAL学习笔记 6 - ADC

1. 初始化ADC

1.1 使能RCU

1.2 设置频率

1.3 设置分辨率

1.4 设置数据对齐模式

1.5 扫描模式

1.6 设置触发模式

1.6 使能ADC

2. 通道规则的初始化

3. 通道使能

4. 软件触发使能

5. 获取ADC的值

5.1 等待转换结束

5.2 读入数值

6. 实例

6.1 GPIO初始化

6.2 初始化ADC

6.3 软件触发

6.4 打印ADC结果


GD32F450的ADC最多三个,每个ADC有19个通道(16个外部通道),ADC0的通道16,通道17和通道18分别连接到温度传感器,内部参考电压和VBAT/4模拟输入。 ADC通道16、通道17和通道18连接VSSA。 ADC2.通道16、通道17和通道18连接VSSA。

注意:Datasheet通道数似乎与用户手册不同,但应该是19个通道。

最高分辨率为12、10、8、6位。

采用率最高2.6MSPs(12位分辨率)、3.0MSPs(10位分辨率)。

根据特定的序列,通道可以组织成两种序列:规则通道和注入通道

规则通道:最多16 类似于顺序执行代码的转换序列,ADC硬件按照定义好的顺序(由寄存器RSQ0、RSQ1、RSQ2决定个数和顺序的转换ADC。获得规则通道ADC值更新为寄存器ADC_SYNCDATA因此,如果需要正确阅读多规则通道ADC必须使用值DMA的方式。

注入通道:最多4 类似于中断程序的转换序列将中断规则通道(寄存器)ISQ决定个数和顺序)。注入通道获得的ADC每个通道都有相应的寄存器(ADC_IDATA0 - ADC_IDATA3)。

ADC有许多功能组合。为了便于使用和理解,首先选择最简单的方法:只使用注入通道和软件启动DMA,右对齐数据格式。

1. 初始化ADC

1.1 使能RCU

switch(port) {     case HW_ADC0:         rcu_periph_clock_enable(RCU_ADC0);         break;     case HW_ADC1:         rcu_periph_clock_enable(RCU_ADC1);         break;     case HW_ADC2:         rcu_periph_clock_enable(RCU_ADC2);         break;     case HW_ADC_MAX:         return; }

1.2 设置频率

ADC设置同步控制寄存器的频率设置 ADC_SYNCCT的16-18位。

PCLK2最大100MHz,HCLK最大200M,对应ADC的时钟为:50M,25M,16.67M,12.5M,40M,33.33M, 20M,10M。取前四种设置,以适应其他芯片的设置。

switch(clkDiv) {     case ADC_CLK_DIV2:     default:         adc_clock_config(ADC_ADCCK_PCLK2_DIV2);         break;     case ADC_CLK_DIV4:         adc_clock_config(ADC_ADCCK_PCLK2_DIV4);         break;     case ADC_CLK_DIV6:         adc_clock_config(ADC_ADCCK_PCLK2_DIV6);         break;     case ADC_CLK_DIV8:         adc_clock_config(ADC_ADCCK_PCLK2_DIV8);         break; }

1.3 设置分辨率

switch(resolutin) {     case ADC_RESOLUTION_8BIT:         adc_resolution_config(adcGroup[port], ADC_RESOLUTION_12B);         break;     case ADC_RESOLUTION_10BIT:         adc_resolution_config(adcGroup[port], ADC_RESOLUTION_10B);         break;     case ADC_RESOLUTION_6BIT:         adc_resolution_config(adcGroup[port], ADC_RESOLUTION_6B);         break;     case ADC_RESOLUTION_12BIT:     default:         adc_resolution_config(adcGroup[port], ADC_RESOLUTION_12B);         break; }

1.4 设置 数据对齐方式

adc_data_alignment_config(adcGroup[port], ADC_DATAALIGN_RIGHT)

默认选择右对齐(最常用的方式)

1.5 使能扫描模式

adc_special_function_config(adcGroup[port], ADC_SCAN_MODE, ENABLE);

1.6 设置触发模式

adc_external_trigger_config(adcGroup[port], ADC_INSERTED_CHANNEL, DISABLE);
adc_external_trigger_config(adcGroup[port], ADC_REGULAR_CHANNEL, DISABLE);

 默认将2种规则通道的触发都Disable,由软件启动或另外的API函数设置对应的触发方式。

1.6 使能ADC

adc_enable(adcGroup[port]);
adc_calibration_enable(adcGroup[port]);

 使能ADC并使能校准功能。

2. 初始化通道规则

void adcCHInit(uint8_t port, uint8_t *pRegualer, uint8_t regualerLen, uint8_t *pInserted, uint8_t insertedLen)
{
    uint8_t i;
    if(port > HW_ADC_MAX)
        return;
    
    adc_channel_length_config(adcGroup[port], ADC_REGULAR_CHANNEL, regualerLen);
    for (i = 0; i < regualerLen; i++)
    {
        adc_regular_channel_config(adcGroup[port], i, pRegualer[i], ADC_SAMPLETIME_480);
    }
    
    adc_channel_length_config(adcGroup[port], ADC_INSERTED_CHANNEL, insertedLen);
    for (i = 0; i < insertedLen; i++)
    {
        adc_inserted_channel_config(adcGroup[port], i, pInserted[i], ADC_SAMPLETIME_480);
    }
}

定义哪些通道属于规则通道还是注入通道,这里默认采样率为480个时钟。

3.  通道使能

if(ch > 15)
{
    if(port == HW_ADC0)
    {
        if(ch == 18)
            adc_channel_16_to_18(ADC_VBAT_CHANNEL_SWITCH, ENABLE);
        else 
            adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH, ENABLE);
    }
    else
        return;
}

ADC0的通道16,17,18是特殊的,需要特殊使能。

4. 软件触发使能

void adcSoftwareTrigger(uint8_t port, uint8_t ch)
{
    uint8_t i;
    adc_flag_clear(adcGroup[port], ADC_FLAG_EOC);
    adc_flag_clear(adcGroup[port], ADC_FLAG_EOIC);
    for (i = 0; i < 4; i++)
    {
        if(ch == ((ADC_ISQ(adcGroup[port]) >> (5 * i)) & 0x1F))
        {
            adc_software_trigger_enable(adcGroup[port], ADC_INSERTED_CHANNEL);
            return;
        }
    }
    for (i = 0; i < 6; i++)
    {
        if(ch == ((ADC_RSQ2(adcGroup[port]) >> (5 * i)) & 0x1F))
        {
            adc_software_trigger_enable(adcGroup[port], ADC_REGULAR_CHANNEL);
            return;
        }
    }
    for (i = 0; i < 6; i++)
    {
        if(ch == ((ADC_RSQ1(adcGroup[port]) >> (5 * i)) & 0x1F))
        {
            adc_software_trigger_enable(adcGroup[port], ADC_REGULAR_CHANNEL);
            return;
        }
    }
    for (i = 0; i < 4; i++)
    {
        if(ch == ((ADC_RSQ0(adcGroup[port]) >> (5 * i)) & 0x1F))
        {
            adc_software_trigger_enable(adcGroup[port], ADC_REGULAR_CHANNEL);
            return;
        }
    }
}

根据初始化通道规则配置判断通道属于哪种规则,然后使能对应的触发。

每次触发前需要先把EOC(规则通道转换完成)和EOIC(注入通道转换完成)清0。

5.  获取ADC的值

uint16_t adcGetValue(uint8_t port, uint8_t ch)
{
    uint16_t adcValue = 0;
    uint8_t i;
    uint16_t timeout = 0;
    uint8_t isqIndex = 0;

    for (i = 0; i < 4; i++)
    {
        if(ch == ((ADC_ISQ(adcGroup[port]) >> (5 * i)) & 0x1F))
        {
            while(adc_flag_get(adcGroup[port], ADC_FLAG_EOIC) == 0)
            {
                delayms(1);
                timeout++;
                if(timeout > 2000)
                    return 0;
            }
            adcValue = adc_inserted_data_read(adcGroup[port], isqIndex);
            return adcValue;
        }
        if(((ADC_ISQ(adcGroup[port]) >> (5 * i)) & 0x1F) > 0)
            isqIndex++;
    }
    
    while(adc_flag_get(adcGroup[port], ADC_FLAG_EOC) == 0)
    {
        delayms(1);
        timeout++;
        if(timeout > 2000)
            return 0;
    }
    adcValue = adc_regular_data_read(adcGroup[port]);
    return adcValue;
}

这里有个奇怪的设定,寄存器ADC_ISQ中通道序列排列比较特别,比如这里ADC_ISQ的值为0x294600, 从高位到低位的含义以此为:0x2表示3组注入通道, 通道18,通道17,通道16,0b00000表示没使用。

另外,官方的用户手册EOC的描述也有问题。

5.1 等待转换结束

通过读取寄存器ADC_STAT的EOC是否为1判断ADC转换是否结束。等待EOC为1后需要软件清0。

5.2 读入数值

 先判断当前通道是否是注入通道,如果是,则读入对应注入通道的ADC值(寄存器ADC_IDATA0 - ADC_IDATA3)。

多规则通道如果需要每个通道正确的读到ADC值,必须采用DMA的方式。这里只读入ADC_RDATA的值,只会是最后一个通道的ADC值。

6. 实例

GD32450i-EVAL的外部输入是ADC通道13,GPIOC3.

6.1 GPIO初始化

#define ADC_VR1                         ADC0
#define ADC_CH_VR1                      GPIOC
#define PIN_ADC_CH_VR1                  3

#define ADCCHSetANI()                   gpio_mode_set(IO_ADC_CH_VR1, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, ((uint32_t)1 << PIN_ADC_CH_VR1))

6.2 初始化ADC

这里把外部输入设为规则通道,其他3个内部的ADC通道16,17,18设置为注入通道。

#define ADC_CH_TEMP                     16
#define ADC_CH_VREF                     17
#define ADC_CH_VBAT                     18

uint8_t Inserted[3];
uint8_t Regualer[1];
Regualer[0] = ADC_CH_VR1;
Inserted[0] = ADC_CH_TEMP;
Inserted[1] = ADC_CH_VREF;
Inserted[2] = ADC_CH_VBAT;
adcCHInit(ADC_VR1, Regualer, 1, Inserted, 3);
adcCHEnable(ADC_VR1, ADC_CH_VR1, ADC_SAMPLETIME_144);
adcCHEnable(HW_ADC0, ADC_CH_TEMP, ADC_SAMPLETIME_144);
adcCHEnable(HW_ADC0, ADC_CH_VREF, ADC_SAMPLETIME_144);
adcCHEnable(HW_ADC0, ADC_CH_VBAT, ADC_SAMPLETIME_144);
adcInit(ADC_VR1, ADC_CLK_DIV2, ADC_RESOLUTION_12BIT);

6.3 软件触发

adcSoftwareTrigger(ADC_VR1, ADC_CH_VR1);
adcSoftwareTrigger(HW_ADC0, ADC_CH_TEMP);
adcSoftwareTrigger(HW_ADC0, ADC_CH_VREF);
adcSoftwareTrigger(HW_ADC0, ADC_CH_VBAT);

6.4 打印ADC结果

Printf("VR1 Value:%d mV\r\n", (uint32_t)(((float)adcGetValue(ADC_VR1, ADC_CH_VR1) / 4095) * 3.3f * 1000));
Printf("Temperature Value:%d mC\r\n",  (uint32_t)((1.42f - (float)adcGetValue(HW_ADC0, ADC_CH_TEMP) * 3.3f / 4096) * 1000 / 4.35f + 25) * 1000);
Printf("Reference Voltage:%d mV\r\n", (uint32_t)(((float)adcGetValue(HW_ADC0, ADC_CH_VREF) / 4095) * 3.3f * 1000));
Printf("VBAT Voltage:%d mV\r\n", (uint32_t)(((float)adcGetValue(HW_ADC0, ADC_CH_VBAT) / 4095) * 3.3f * 4 * 1000));

得到的打印结果如下:

************ ADC Result ************* VR1 Value:1481 mV Temperature Value:35000 mC Reference Voltage:1200 mV VBAT Voltage:3284 mV

扭动VR1可以看到VR1 Value的值会变化。

标签: f450光电开关传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台