[开发板与模块]

【ESK32-30519 + ESK32-21001测评】ADC+PDMA采集数据并显示

[复制链接]
78|8
手机看帖
扫描二维码
随时随地手机跟帖
lgnstar|  楼主 | 2022-9-21 09:57 | 显示全部楼层 |阅读模式
本帖最后由 lgnstar 于 2022-9-21 10:04 编辑

首先感谢21ic和合泰能给我这次测评的机会!
此次测试内容为使用ADC采集模拟数据并显示在4位数码管上,采用ADC+PDMA方式,模拟量使用ESK32-21001底板的Potentiometer电位计获得。
核心板需要与底板连接3根线, VDD33、VSS、Potentiometer(VR)。我在焊连接器插针的时候脑子短路,把连接器焊反了,只能使用连接线了
20220921082137.jpg
项目工程直接使用\HT32_STD_5xxxx_FWLib_V1.1.1_5938\project_template\IP\Example\MDK_ARMv5\Project_54253.uvprojx修改。
这里使用软件触发ADC,PDMA循环模式,初始化程序如下:
static void PDMA_Configuration(void)
{
    { /* Enable peripheral clock                                                                              */
      CKCU_PeripClockConfig_TypeDef CKCUClock = {{ 0 }};
      CKCUClock.Bit.PDMA = 1;
      CKCU_PeripClockConfig(CKCUClock, ENABLE);
    }

    { /* Configure PDMA channel to move ADC result from ADC->DR[0] to TM CHnCCR                               */
      PDMACH_InitTypeDef PDMACH_InitStructure;
      PDMACH_InitStructure.PDMACH_SrcAddr = (u32)&HTCFG_ADC_PORT->DR[0];
      PDMACH_InitStructure.PDMACH_DstAddr = (u32)(adc_buf);   //采样数据缓冲区
      PDMACH_InitStructure.PDMACH_BlkCnt = 1;
      PDMACH_InitStructure.PDMACH_BlkLen = ADC_SAMPLE_CNT;    //连续采样数据个数
      PDMACH_InitStructure.PDMACH_DataSize = WIDTH_16BIT;
      PDMACH_InitStructure.PDMACH_Priority = VH_PRIO;
      PDMACH_InitStructure.PDMACH_AdrMod = SRC_ADR_FIX | DST_ADR_LIN_INC | AUTO_RELOAD;
      PDMA_Config(HTCFG_DMA_PORT, &PDMACH_InitStructure);
      PDMA_EnaCmd(HTCFG_DMA_PORT, ENABLE);
    }
}

static void ADC_Configuration(void)
{
    { /* Enable peripheral clock                                                                              */
      CKCU_PeripClockConfig_TypeDef CKCUClock = {{ 0 }};
      CKCUClock.Bit.AFIO = 1;
      CKCUClock.Bit.HTCFG_ADC_IPN = 1;
      CKCU_PeripClockConfig(CKCUClock, ENABLE);
    }
    /* Configure AFIO mode as ADC function                                                                    */
    AFIO_GPxConfig(HTCFG_VR_GPIO_ID, HTCFG_VR_AFIO_PIN, HTCFG_VR_AFIO_MODE);
   
    { /* ADC related settings                                                                                 */
      /* CK_ADC frequency is set to (CK_AHB / 64)                                                             */
      CKCU_SetADCnPrescaler(HTCFG_ADC_CKCU_ADCPRE, CKCU_ADCPRE_DIV64);

      /* One Shot mode, sequence length = 1                                                                   */
      //ADC_RegularGroupConfig(HTCFG_ADC_PORT, ONE_SHOT_MODE, 1, 0);
      ADC_RegularGroupConfig(HTCFG_ADC_PORT, CONTINUOUS_MODE, 1, 0);

      /* ADC conversion time = (Sampling time + Latency) / CK_ADC = (1.5 + ADST + 12.5) / CK_ADC              */
      /* Set ADST = 0, sampling time = 1.5 + ADST                                                             */
      #if (LIBCFG_ADC_SAMPLE_TIME_BY_CH)
        // The sampling time is set by the last parameter of the function "ADC_RegularChannelConfig()".
      #else
      ADC_SamplingTimeConfig(HTCFG_ADC_PORT, 0);
      #endif

      /* Set ADC conversion sequence as channel n                                                             */
      ADC_RegularChannelConfig(HTCFG_ADC_PORT, HTCFG_VR_ADC_CH, 0, 0);

      #if 1
      /* Set TM MTO as ADC trigger source                                                                     */
      //ADC_RegularTrigConfig(HTCFG_ADC_PORT, HTCFG_ADC_TRIG_TM);
      ADC_RegularTrigConfig(HTCFG_ADC_PORT, ADC_TRIG_SOFTWARE);       //使能软件触发转换
      #endif
    }

    /* Issue ADC DMA request when cycle end of conversion occur                                               */
    ADC_PDMAConfig(HTCFG_ADC_PORT, ADC_PDMA_REGULAR_CYCLE, ENABLE);   //使能连续模式

    ADC_Cmd(HTCFG_ADC_PORT, ENABLE);
    ADC_SoftwareStartConvCmd(HTCFG_ADC_PORT, ENABLE);                 //触发ADC开始转换
}
ADC转换开始后就会不间断的进行转换,转换结果会放入adc_buf缓冲区,循环更新。
接下来及就是在数码管上显示ADC转换的结果。这里只显示ADC转换后的值(0~4095)。
const u8 NumberChar[]={
  /* a: Seg4(0x10), b: Seg7(0x80), c: Seg2(0x04), d: Seg5(0x20),
     e: Seg3(0x08), f: Seg0(0x01), g: Seg6(0x40), dp: Seg1(0x02) */
  0xBD, /* '0' = a+b+c+d+e+f      = 0x10 + 0x80 + 0x04 + 0x20 + 0x08 + 0x01               = 0xBD */
  0x84, /* '1' =   b+c+           =        0x80 + 0x04                                    = 0x84 */
  0xF8, /* '2' = a+b  +d+e  +g    = 0x10 + 0x80 +        0x20 + 0x08 +      + 0x40        = 0xF8 */
  0xF4, /* '3' = a+b+c+d    +g    = 0x10 + 0x80 + 0x04 + 0x20 +             + 0x40        = 0xF4 */
  0xC5, /* '4' =   b+c    +f+g    =        0x80 + 0x04               + 0x01 + 0x40        = 0xC5 */
  0x75, /* '5' = a  +c+d  +f+g    = 0x10        + 0x04 + 0x20        + 0x01 + 0x40        = 0x75 */
  0x7D, /* '6' = a  +c+d+e+f+g    = 0x10        + 0x04 + 0x20 + 0x08 + 0x01 + 0x40        = 0x7D */
  0x95, /* '7' = a+b+c    +f      = 0x10 + 0x80 + 0x04               + 0x01               = 0x95 */
  0xFD, /* '8' = a+b+c+d+e+f+g    = 0x10 + 0x80 + 0x04 + 0x20 + 0x08 + 0x01 + 0x40        = 0xFD */
  0xF5, /* '9' = a+b+c+d  +f+g    = 0x10 + 0x80 + 0x04 + 0x20        + 0x01 + 0x40        = 0xF5 */
  0x02  /* '.' =               dp =                                                  0x02 = 0x02 */
};


/*********************************************************************************************************//**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  Configure the LEDC.
  * @retval None
  ***********************************************************************************************************/
void bsp_ledc_config(void)
{
  {
    /* Turn on the APB clock of AFIO. */
    CKCU_PeripClockConfig_TypeDef CKCUClock = {{0}};
    CKCUClock.Bit.AFIO       = 1;
    CKCU_PeripClockConfig(CKCUClock, ENABLE);

    /* SEG0~7 */
    AFIO_GPxConfig(HTCFG_LEDC_SEG0_GPIO_ID, HTCFG_LEDC_SEG0_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG1_GPIO_ID, HTCFG_LEDC_SEG1_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG2_GPIO_ID, HTCFG_LEDC_SEG2_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG3_GPIO_ID, HTCFG_LEDC_SEG3_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG4_GPIO_ID, HTCFG_LEDC_SEG4_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG5_GPIO_ID, HTCFG_LEDC_SEG5_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG6_GPIO_ID, HTCFG_LEDC_SEG6_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_SEG7_GPIO_ID, HTCFG_LEDC_SEG7_AFIO_PIN, AFIO_FUN_LEDC);

    /* COM0~4 */
    AFIO_GPxConfig(HTCFG_LEDC_COM0_GPIO_ID, HTCFG_LEDC_COM0_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_COM1_GPIO_ID, HTCFG_LEDC_COM1_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_COM2_GPIO_ID, HTCFG_LEDC_COM2_AFIO_PIN, AFIO_FUN_LEDC);
    AFIO_GPxConfig(HTCFG_LEDC_COM3_GPIO_ID, HTCFG_LEDC_COM3_AFIO_PIN, AFIO_FUN_LEDC);
  }

  {
    LEDC_InitTypeDef LEDC_InitStruct;
    /* Turn on the APB clock of LEDC. */
    CKCU_PeripClockConfig_TypeDef CKCUClock = {{0}};
    CKCUClock.Bit.LEDC       = 1;
    CKCU_PeripClockConfig(CKCUClock, ENABLE);

    /* Frame = fCK_LED / (LEDC_DutyClockNumber * COM Number) */
    /*       = 1600 / (8*4)  =  50Hz */
    LEDC_InitStruct.LEDC_ClockSource    = LEDC_SRC_LSI;                    /* LSI = 32Khz */
    LEDC_InitStruct.LEDC_ClockPrescaler = HTCFG_LEDC_CLOCK_PRESCAL - 1;    /* fCK_LED = 32Khz / 20 = 1600 Hz */
    LEDC_InitStruct.LEDC_DutyClockNumber = HTCFG_LEDC_DUTY_CLOCK_NUMBER;   /* Duty CLock Number = 8 Clock */
    LEDC_InitStruct.LEDC_COMxEN = HTCFG_LEDC_COM1EN | HTCFG_LEDC_COM2EN |\
                                  HTCFG_LEDC_COM3EN | HTCFG_LEDC_COM4EN;   /* Enable COMx */

    /* Change the brightness, the parameter range is 0~Duty (CLock Number-1.) */
    /* 0: the brightest, 7: the darkest.                                      */
    LEDC_InitStruct.LEDC_DeadTime = 0;
    LEDC_Init(&LEDC_InitStruct);                                            /* LEC initialization. */

    /* Set the LEDC mode to COMMON_CATHODE. */
    LEDC_SetPolarityMode(HTCFG_LEDC_COM1POL | HTCFG_LEDC_COM2POL | HTCFG_LEDC_COM3POL | HTCFG_LEDC_COM4POL,
                         LEDC_SEG0POL | LEDC_SEG1POL | LEDC_SEG2POL | LEDC_SEG3POL |\
                         LEDC_SEG4POL | LEDC_SEG5POL | LEDC_SEG6POL | LEDC_SEG7POL,
                         COMMON_CATHODE);

    //LEDC_IntConfig(ENABLE);                                                 /* Enable frame interrupt. */
    //NVIC_EnableIRQ(LEDC_IRQn); /* Enable LECD IRQ. */
    LEDC_Cmd(ENABLE);          /* LEDC Start. */
  }
}

void bsp_ledc_display(u16 val)
{
  LEDC_SetData(HTCFG_LEDC_COM_D1, NumberChar[val/1000%10]);
  LEDC_SetData(HTCFG_LEDC_COM_D2, NumberChar[val/100%10]);
  LEDC_SetData(HTCFG_LEDC_COM_D3, NumberChar[val/10%10]);
  LEDC_SetData(HTCFG_LEDC_COM_D4, NumberChar[val%10]);
}
使用systick产生1ms定时中断,作为软件定时器的基准。
  {
          /* SYSTICK configuration */
    SYSTICK_ClockSourceConfig(SYSTICK_SRC_STCLK);       // Default : CK_AHB/8
    SYSTICK_SetReloadValue(SystemCoreClock / 8 / 1000); // (CK_AHB/8/1000) = 1ms on chip
    SYSTICK_IntConfig(ENABLE);                          // Enable SYSTICK Interrupt
    SYSTICK_CounterCmd(SYSTICK_COUNTER_ENABLE);
  }
定义软件定时器,(此处代码参考安富莱例程)。
//bsp_stimer.h

#ifndef __BSP_STIMER_H
#define __BSP_STIMER_H

#define TMR_COUNT        2                /* 软件定时器的个数 (定时器ID范围 0 - 3) */

#define TMR_REFRESH_DISPLAY   0   //用户更新数码管显示
#define TMR_ADC_SAMPLE        1   //用于ADC滤波,计算转换结果

typedef enum
{
        TMR_MODE_ONCE = 0,                /* 一次工作模式 */
        TMR_MODE_AUTO = 1,          /* 自动定时工作模式 */
        TMR_MODE_STOP = 2,
}tmr_mode_t;

typedef struct
{
        volatile u8   Mode;                    /* 计数器模式,1次性 */
        volatile u8   Flag;                    /* 定时到达标志  */
        volatile u32  Count;            /* 计数器 */
        volatile u32  PreLoad;          /* 计数器预装值 */
}soft_tmr_t;

void bsp_stimer_init(void);
void bsp_stimer_start(u8 _id, u32 _period, tmr_mode_t mode);
void bsp_stimer_stop(u8 _id);
u8 bsp_stimer_check(u8 _id);

void bsp_stimer_dec(void);

#endif
// bsp_stimer.c

#include "ht32.h"
#include "bsp_stimer.h"

/* 定于软件定时器结构体变量 */
static soft_tmr_t s_tTmr[TMR_COUNT] = {0};

/*
*********************************************************************************************************
*        函 数 名: bsp_stimer_init
*        功能说明: 配置systick中断,并初始化软件定时器变量
*        形    参:  无
*        返 回 值: 无
*********************************************************************************************************
*/
void bsp_stimer_init(void)
{
  /* SYSTICK configuration */
  SYSTICK_ClockSourceConfig(SYSTICK_SRC_STCLK);       // Default : CK_AHB/8
  SYSTICK_SetReloadValue(SystemCoreClock / 8 / 1000); // (CK_AHB/8/1000) = 1ms on chip
  SYSTICK_IntConfig(ENABLE);                          // Enable SYSTICK Interrupt
  SYSTICK_CounterCmd(SYSTICK_COUNTER_ENABLE);
}

/*
*********************************************************************************************************
*        函 数 名: bsp_stimer_init
*        功能说明: 每隔1ms对所有定时器变量减1。必须被SysTick_Handler中断函数周期性调用。
*        形    参:
*        返 回 值: 无
*********************************************************************************************************
*/
void bsp_stimer_dec(void)
{
  u8 i;
        for (i = 0; i < TMR_COUNT; i++)
        {
    if(s_tTmr[i].Mode != TMR_MODE_STOP)
    {
      if (s_tTmr[i].Count > 0)
      {
        if (--s_tTmr[i].Count == 0)
        {
          s_tTmr[i].Flag = 1;
          if(s_tTmr[i].Mode == TMR_MODE_AUTO)
          {
            s_tTmr[i].Count = s_tTmr[i].PreLoad;
          }
        }
      }
    }
        }
}

/*
*********************************************************************************************************
*        函 数 名: bsp_stimer_start
*        功能说明: 启动一个定时器,并设置定时周期。
*        形    参:          _id     : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
*                                _period : 定时周期,单位1ms
*       _mode   : 定时器工作模式,一次性、连续
*        返 回 值: 无
*********************************************************************************************************
*/
void bsp_stimer_start(u8 _id, u32 _period, tmr_mode_t _mode)
{
        if (_id >= TMR_COUNT)
        {
                while(1); /* 参数异常,死机等待看门狗复位 */
        }

        __disable_irq();                          /* 关中断 */

        s_tTmr[_id].Count = _period;                /* 实时计数器初值 */
        s_tTmr[_id].PreLoad = _period;  /* 计数器自动重装值,仅自动模式起作用 */
        s_tTmr[_id].Flag = 0;                                    /* 定时时间到标志 */
        s_tTmr[_id].Mode = _mode;              /* 工作模式 */

        __enable_irq();                                  /* 开中断 */
}

/*
*********************************************************************************************************
*        函 数 名: bsp_stimer_stop
*        功能说明: 停止一个定时器
*        形    参:          _id     : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
*        返 回 值: 无
*********************************************************************************************************
*/
void bsp_stimer_stop(u8 _id)
{
        if (_id >= TMR_COUNT)
        {
                while(1); /* 参数异常,死机等待看门狗复位 */
        }

        __disable_irq();                          /* 关中断 */

        s_tTmr[_id].Count = 0;                                /* 实时计数器初值 */
        s_tTmr[_id].Flag = 0;                                /* 定时时间到标志 */
        s_tTmr[_id].Mode = TMR_MODE_STOP;        /* 自动工作模式 */

        __enable_irq();                                  /* 开中断 */
}

/*
*********************************************************************************************************
*        函 数 名: bsp_stimer_check
*        功能说明: 检测定时器是否超时
*        形    参:          _id     : 定时器ID,值域【0,TMR_COUNT-1】。用户必须自行维护定时器ID,以避免定时器ID冲突。
*                                _period : 定时周期,单位1ms
*        返 回 值: 返回 0 表示定时未到, 1表示定时到
*********************************************************************************************************
*/
u8 bsp_stimer_check(u8 _id)
{
        if (_id >= TMR_COUNT)
        {
                return 0;
        }

        if (s_tTmr[_id].Flag == 1)
        {
                s_tTmr[_id].Flag = 0;                return 1;
        }
        else
        {
                return 0;
        }
}
好的,到此各个模块已经准备好了,在main函数中调用这几个模块就可以显示出想要的结果。
int main(void)
{
  bsp_stimer_init();
  bsp_adc_sample();
  bsp_ledc_config();
  bsp_ledc_display(8888);

  HT32F_DVB_LEDInit(HT_LED1);

  bsp_stimer_start(TMR_REFRESH_DISPLAY, 1000, TMR_MODE_AUTO);
  bsp_stimer_start(TMR_ADC_SAMPLE, 50, TMR_MODE_AUTO);

  while (1)
  {
    if(bsp_stimer_check(TMR_ADC_SAMPLE))
    {
      bsp_adc_chache();
    }

    if(bsp_stimer_check(TMR_REFRESH_DISPLAY))
    {
      u16 adc_val;
      adc_val = get_adc_value();
      bsp_ledc_display(adc_val);

      HT32F_DVB_LEDToggle(HT_LED1);
    }
  }
}
左右旋转Potentiometer电位器,在数码管上就会显示出当前ADC转换值。





使用特权

评论回复
eltonchang2001| | 2022-9-26 10:57 | 显示全部楼层
楼主排针焊错方向,改用飞线,累了。

使用特权

评论回复
beacherblack| | 2022-10-1 16:56 | 显示全部楼层
PDMA的控制器?                 

使用特权

评论回复
typeof| | 2022-10-1 17:09 | 显示全部楼层
这个学一学,dma的配置不容易呢

使用特权

评论回复
usysm| | 2022-10-1 17:30 | 显示全部楼层
可以实现多少路ADC的采集              

使用特权

评论回复
aspoke| | 2022-10-1 18:13 | 显示全部楼层
PDMA和DMA有什么区别呢         

使用特权

评论回复
lgnstar|  楼主 | 2022-10-3 09:36 | 显示全部楼层
本帖最后由 lgnstar 于 2022-10-3 09:47 编辑
usysm 发表于 2022-10-1 17:30
可以实现多少路ADC的采集

手册上有提到:     单片机内建一个带参考电压发生器 (V REF ) 的12-bit 多通道 A/D 转换器,总共有 14 个复用通
道,包括10 个可供外部模拟信号输入的外部通道和 4 个内部通道。
    A/D 转换器支持14 个多路复用通道,转换结果依组转换次序存放到对应数据寄存器中。一
个转换组可以安排一个转换序列,此序列长度可为1 到 8 个通道。
   我理解使用dma一次最多可以转换8路adc。

使用特权

评论回复
caigang13| | 2022-10-3 11:49 | 显示全部楼层
看看楼主的经验

使用特权

评论回复
Henryko| | 2022-10-4 19:51 | 显示全部楼层
dma的配置很麻烦吧

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则