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最多有3个,每个ADC有19个通道(16个外部通道),ADC0的通道16,通道17和通道18分别连接到温度传感器,内部参考电压和VBAT/4模拟输入。 ADC1的通道16,通道17和通道18内部都连接到VSSA。 ADC2的通道16,通道17和通道18内部都连接到VSSA。

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

最高12位分辨率,可配置为12、10、8、6位分辨率。

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

通道可以按照特定的序列组织为2种方式的序列:规则通道和注入通道

规则通道:最多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。为了兼容其他芯片的设定,取前面4种设定。

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的值会变化。

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值