FuShenxiao 发表于 2024-8-12 16:04

小熊派BearPi-Pico H2821星闪开发板测评(三)——ADC与DMA测试

<p><strong><span style="font-size:18px;">ADC采样测试</span></strong></p>

<p>对于H2821,GPIO02和GPIO03可复用为模拟电压输入,因此将这两个引脚接入电位器用于ADC采样测试。</p>

<p style="text-align: center;"> &nbsp;</p>

<p style="text-align: justify;">代码实现:</p>

<pre>
<code>#include "pinctrl.h"
#include "gpio.h"
#include "adc.h"
#include "adc_porting.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"
#include "pm_pmu.h"

#define ADC_TASK_STACK_SIZE               0x1000
#define ADC_TASK_PRIO                     (osPriority_t)(17)
#define ADC_AUTO_SAMPLE_TEST_TIMES      1000

#define GAFE_SAMPLE_VALUE_SIGN_BIT      17
#define ADC_REFERENCE_VOLTAGE_MV          1500
#define ADC_REF_VOL_DIFFERENCE_MULT       2
#define ADC_TICK2VOL_REF_VOLTAGE_MV       (ADC_REFERENCE_VOLTAGE_MV * ADC_REF_VOL_DIFFERENCE_MULT)


static void adc_set_io(uint8_t channel)
{
    pin_t adc_pin[] = {S_MGPIO2, S_MGPIO3, S_MGPIO4, S_MGPIO5, S_MGPIO28, S_MGPIO29, S_MGPIO30, S_MGPIO31};

    uapi_pin_set_mode(adc_pin, PIN_MODE_0);
    uapi_gpio_set_dir(adc_pin, GPIO_DIRECTION_INPUT);
    uapi_pin_set_pull(adc_pin, PIN_PULL_NONE);
#if defined(CONFIG_PINCTRL_SUPPORT_IE)
    uapi_pin_set_ie(adc_pin, PIN_IE_1);
#endif
}


static void *adc_task(const char *arg)
{
    UNUSED(arg);

    uapi_pmu_control(PMU_CONTROL_MICLDO_POWER, PMU_CONTROL_POWER_ON);
    uapi_pmu_ldo_set_voltage(PMU_LDO_ID_MICLDO, PMU_MICLDO_VSET_1V5);//在MLDO脚产生一个1.5V的电压便以测试
    osal_printk("start adc sample test\n");
    uapi_adc_init(ADC_CLOCK_NONE);
    adc_set_io(GADC_CHANNEL_0);
    uapi_adc_open_channel(GADC_CHANNEL_0);//GPIO02
    uapi_adc_power_en(AFE_GADC_MODE, true);
    adc_calibration(AFE_GADC_MODE, true, true, true);

    while (1)
    {
      int gadc_value = 0;
      gadc_value =uapi_adc_auto_sample(GADC_CHANNEL_0);
      osal_printk("gadc: %dmv\n\n", (gadc_value * ADC_TICK2VOL_REF_VOLTAGE_MV) &gt;&gt; GAFE_SAMPLE_VALUE_SIGN_BIT);
      osDelay(2000);
    }
    return NULL;
}

static void adc_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "ADCTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = ADC_TASK_STACK_SIZE;
    attr.priority = ADC_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)adc_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the adc_entry. */
app_run(adc_entry);
</code></pre>

<p>其中</p>

<p>uapi_pmu_control用于使能LDO</p>

<p>uapi_pmu_ldo_set_voltage用于使MIC_LDO引脚产生一个指定的电压(可以产生标准的1.1V,1.3V,1.5V,1.8V,2.0V,2.2V,2.5V,2.8V电压)用于测试</p>

<p style="text-align: center;"> &nbsp;</p>

<p>得到串口输出</p>

<p style="text-align: center;"></p>

<p style="text-align: justify;">可见在电位器的控制下,电压逐渐升高</p>

<p style="text-align: justify;"><strong>特别的,当电压高于1.5V时,ADC的值将不再变化,观察ADC值的计算方法,发现</strong></p>

<p style="text-align: center;">gadc = gadc_value *&nbsp;ADC_TICK2VOL_REF_VOLTAGE_MV) &gt;&gt; GAFE_SAMPLE_VALUE_SIGN_BIT</p>

<p style="text-align: justify;">其中</p>

<p style="text-align: justify;">gadc_value为ADC接口测量值</p>

<p style="text-align: justify;">ADC_TICK2VOL_REF_VOLTAGE_MV为基准值,宏定义为3000</p>

<p style="text-align: justify;">GAFE_SAMPLE_VALUE_SIGN_BIT为右移位数,宏定义为17</p>

<p style="text-align: justify;">虽然这个表达式并不能看出芯片对ADC电压的限制,但实际电路应该是对ADC输入做了限制,因为就算我调整了基准电压值,最大有效输入电压仍为1.5V</p>

<p style="text-align: justify;"><strong>除此之外,对比万用表测量输入电压和ADC采样得到的值,ADC采样结果相较万用表小了大约50mV,因此实际程序运行时可能需要多加注意并进行修正</strong></p>

<p style="text-align: justify;">&nbsp;</p>

<p style="text-align: justify;">为了实现多路ADC采样,需要在主函数循环部分的每次采样前都打开采样通道</p>

<p style="text-align: center;"> &nbsp;</p>

<p style="text-align: justify;">若没有在循环部分中打开采样通道,两路ADC采样的结果都将取决于编号较大的模拟采样通道(比如GPIO02的模拟采样通道是AIN0,GPIO03的模拟采样通道是AIN1,如果不打开采样通道就进行采样,AIN0和AIN1的采样结果都将取决于AIN1)。当然,对于单个ADC采样而言,在循环体中是否打开采样通道是无所谓的,仅需在初始化时打开即可。</p>

<p style="text-align: justify;">代码实现:</p>

<pre>
<code>#include "pinctrl.h"
#include "gpio.h"
#include "adc.h"
#include "adc_porting.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"
#include "pm_pmu.h"

#define ADC_TASK_STACK_SIZE               0x1000
#define ADC_TASK_PRIO                     (osPriority_t)(17)
#define ADC_AUTO_SAMPLE_TEST_TIMES      1000

#define GAFE_SAMPLE_VALUE_SIGN_BIT      17
#define ADC_REFERENCE_VOLTAGE_MV          1500
#define ADC_REF_VOL_DIFFERENCE_MULT       2
#define ADC_TICK2VOL_REF_VOLTAGE_MV       (ADC_REFERENCE_VOLTAGE_MV * ADC_REF_VOL_DIFFERENCE_MULT)


static void adc_set_io(uint8_t channel)
{
    pin_t adc_pin[] = {S_MGPIO2, S_MGPIO3, S_MGPIO4, S_MGPIO5, S_MGPIO28, S_MGPIO29, S_MGPIO30, S_MGPIO31};
    uapi_pin_set_mode(adc_pin, PIN_MODE_0);
    uapi_gpio_set_dir(adc_pin, GPIO_DIRECTION_INPUT);
    uapi_pin_set_pull(adc_pin, PIN_PULL_NONE);
#if defined(CONFIG_PINCTRL_SUPPORT_IE)
    uapi_pin_set_ie(adc_pin, PIN_IE_1);
#endif
}


static void *adc_task(const char *arg)
{
    UNUSED(arg);

    uapi_pmu_control(PMU_CONTROL_MICLDO_POWER, PMU_CONTROL_POWER_ON);
    uapi_pmu_ldo_set_voltage(PMU_LDO_ID_MICLDO, PMU_MICLDO_VSET_1V5);//在MLDO脚产生一个1.5V的电压便以测试
    osal_printk("start adc sample test\n");
    uapi_adc_init(ADC_CLOCK_NONE);
    adc_set_io(GADC_CHANNEL_0);
    adc_set_io(GADC_CHANNEL_1);
    uapi_adc_open_channel(GADC_CHANNEL_0);//GPIO02
    uapi_adc_open_channel(GADC_CHANNEL_1);//GPIO03
    uapi_adc_power_en(AFE_GADC_MODE, true);
    adc_calibration(AFE_GADC_MODE, true, true, true);

    while (1)
    {
      int gadc_value1 = 0;
      int gadc_value2 = 0;
      uapi_adc_open_channel(GADC_CHANNEL_0);//GPIO02
      gadc_value1 =uapi_adc_auto_sample(GADC_CHANNEL_0);
      uapi_adc_open_channel(GADC_CHANNEL_1);//GPIO03
      gadc_value2 = uapi_adc_auto_sample(GADC_CHANNEL_1);
      osal_printk("gadc1: %dmv\n\n", (gadc_value1 * ADC_TICK2VOL_REF_VOLTAGE_MV) &gt;&gt; GAFE_SAMPLE_VALUE_SIGN_BIT);
      osal_printk("gadc2: %dmv\n\n", (gadc_value2 * ADC_TICK2VOL_REF_VOLTAGE_MV) &gt;&gt; GAFE_SAMPLE_VALUE_SIGN_BIT);
      osDelay(2000);
    }
    return NULL;
}

static void adc_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "ADCTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = ADC_TASK_STACK_SIZE;
    attr.priority = ADC_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)adc_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the adc_entry. */
app_run(adc_entry);
</code></pre>

<p style="text-align: justify;">输出结果:</p>

<p style="text-align: center;"> &nbsp;</p>

<p style="text-align: justify;"><strong><span style="font-size:18px;">DMA内存访问测试</span></strong></p>

<p style="text-align: justify;">DAM用于内存数据的搬运,从而减少CPU的负担</p>

<p style="text-align: justify;">代码实现如下:</p>

<pre>
<code>#include "securec.h"
#include "common_def.h"
#include "dma.h"
#include "hal_dma.h"
#include "dma_porting.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"

#define DMA_TRANSFER_WORD_NUM       32
#define DMA_TRANSFER_PRIORITY       0
#define DMA_TRANSFER_WIDTH          2

#define DMA_TASK_STACK_SIZE         0x1000
#define DMA_TASK_DURATION_MS      500
#define DMA_TASK_PRIO               (osPriority_t)(17)

static uint32_t g_app_dma_src_data = { 0 };
static uint32_t g_app_dma_desc_data = { 0 };
static uint8_t g_dma_trans_done = 0;

static void app_dma_trans_done_callback(uint8_t int_type, uint8_t channel, uintptr_t arg)
{
    unused(arg);
    unused(channel);
    switch (int_type) {
      case HAL_DMA_INTERRUPT_TFR:
            g_dma_trans_done = 1;
            break;
      case HAL_DMA_INTERRUPT_BLOCK:
            g_dma_trans_done = 1;
            break;
      case HAL_DMA_INTERRUPT_ERR:
            osal_printk("DMA transfer error.\r\n");
            break;
      default:
            break;
    }
}

static void *dma_task(const char *arg)
{
    unused(arg);
    /* DMA init. */
    dma_port_clock_enable();
    uapi_dma_init();
    uapi_dma_open();

#if defined(CONFIG_DMA_MEMORY_LLI_TRANSFER_MODE)
    dma_channel_t dma_channel = DMA_CHANNEL_NONE;
    dma_channel = uapi_dma_get_lli_channel(0, HAL_DMA_HANDSHAKING_MAX_NUM);
#endif

    for (uint32_t i = 0; i &lt; DMA_TRANSFER_WORD_NUM; i++) {
      g_app_dma_src_data = i;
    }
    memset_s(g_app_dma_desc_data, DMA_TRANSFER_WORD_NUM, 0, DMA_TRANSFER_WORD_NUM);

    dma_ch_user_memory_config_t transfer_config = { 0 };
    transfer_config.src = (uint32_t)(uintptr_t)g_app_dma_src_data;
    transfer_config.dest = (uint32_t)(uintptr_t)g_app_dma_desc_data;
    transfer_config.transfer_num = DMA_TRANSFER_WORD_NUM;
    transfer_config.priority = DMA_TRANSFER_PRIORITY;
    transfer_config.width = DMA_TRANSFER_WIDTH;

    while (1) {
      osDelay(DMA_TASK_DURATION_MS);
      g_dma_trans_done = 0;
#if defined(CONFIG_DMA_MEMORY_LLI_TRANSFER_MODE)
      osal_printk("dma config link list item of memory to memory start!\r\n");
      if (uapi_dma_transfer_memory_lli(dma_channel, &amp;transfer_config, app_dma_trans_done_callback) == ERRCODE_SUCC) {
            osal_printk("dma config link list item of memory to memory succ!\r\n");
      }
      osal_printk("dma enable lli memory transfer start!\r\n");
      if (uapi_dma_enable_lli(dma_channel, app_dma_trans_done_callback, (uintptr_t)NULL) == ERRCODE_SUCC) {
            osal_printk("dma enable lli memory transfer succ!\r\n");
      }
      while (!g_dma_trans_done) {}
      if (uapi_dma_end_transfer(dma_channel) == ERRCODE_SUCC) {
            osal_printk("dma channel transfer finish!\r\n");
      }
#else
      osal_printk("dma single memory transfer start!\r\n");
      if (uapi_dma_transfer_memory_single(&amp;transfer_config, app_dma_trans_done_callback,
                                          (uintptr_t)NULL) == ERRCODE_SUCC) {
            osal_printk("dma single memory transfer succ!\r\n");
      }
      while (!g_dma_trans_done) {}
      osal_printk("dma checking transfer from 0x%08x to 0x%08x...\r\n", transfer_config.src, transfer_config.dest);
      if (memcmp((void *)transfer_config.src, (void *)transfer_config.dest, transfer_config.transfer_num) == 0) {
            osal_printk("dma memory copy test succ, length = %d block\r\n", transfer_config.transfer_num);
      }
#endif
    }

    return NULL;
}

static void dma_entry(void)
{
    osThreadAttr_t attr;

    attr.name = "DmaTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = DMA_TASK_STACK_SIZE;
    attr.priority = DMA_TASK_PRIO;

    if (osThreadNew((osThreadFunc_t)dma_task, NULL, &amp;attr) == NULL) {
      /* Create task fail. */
    }
}

/* Run the dma_entry. */
app_run(dma_entry);
</code></pre>

<p style="text-align: justify;">结果显示:</p>

<p style="text-align: center;"> &nbsp; &nbsp;</p>
页: [1]
查看完整版本: 小熊派BearPi-Pico H2821星闪开发板测评(三)——ADC与DMA测试