打印
[AT32F423]

3【AT-START-F423测评】多路电压采集系统

[复制链接]
1516|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lulugl|  楼主 | 2023-11-7 20:43 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
#申请原创# #有奖活动#[url=home.php?mod=space&uid=760190]@21小跑堂 [/url]
【项目简介】
在工业冶具行业中,经常会有多路电压测试的需求,如下图所示:

这种解决方案,虽然适配简单,但是如果对其进行人工判断,一来观测的人容易产生误差,而且需要人工值守,成本也比较高。
而用AT32F423的ADC,可以实现一个单片机对应测量24路电压。并且可以通过设定正常电压范围,就可以实现智能的电压监测,对于异常情况可以通过多种途径进行报警提示,后台数据记录等。
【AT32F423的ADC简介】
在数据手册《DS_AT32F423_V2.00_CH.pdf》中如下图所描述:

AT32F423中有一个ADC1,总共26个通道,ADC_IN0 至 ADC_IN15 为外部模拟输入,ADC_IN16 为内部温度传感器,ADC_IN17 为内部参考电压,ADC_IN20 至 ADC_IN27 为外部模拟输入。这样就有24个通道可以做输入检测。
【程序的实现步骤】
使用雅特力的图形化配置工具Work Bench对24个通的ADC进行图形化配置:

在代码预览中复制ADC的配置代码到工程模板中的adc的gpio初始化函数中:


参照adc的DMA例程中添加ADC的配置,具体程序如下:
/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  adc configuration.
  * @param  none
  * @retval none
  */
static void adc_config(void)
{
  adc_common_config_type adc_common_struct;
  adc_base_config_type adc_base_struct;
  crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);
  nvic_irq_enable(ADC1_IRQn, 0, 0);
  crm_adc_clock_select(CRM_ADC_CLOCK_SOURCE_HCLK);

  adc_common_default_para_init(&adc_common_struct);

  /* config division,adcclk is division by hclk */
  adc_common_struct.div = ADC_HCLK_DIV_4;

  /* config inner temperature sensor and vintrv */
  adc_common_struct.tempervintrv_state = FALSE;

  adc_common_config(&adc_common_struct);

  adc_base_default_para_init(&adc_base_struct);

  adc_base_struct.sequence_mode = TRUE;
  adc_base_struct.repeat_mode = FALSE;
  adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT;
  adc_base_struct.ordinary_channel_length = 24;
  adc_base_config(ADC1, &adc_base_struct);
  adc_resolution_set(ADC1, ADC_RESOLUTION_12B);

  /* config ordinary channel */
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_4, 5, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_5, 6, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_6, 7, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_7, 8, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_8, 9, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_9, 10, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_10, 11, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_11, 12, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_12, 13, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_13, 14, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_14, 15, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_15, 16, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_20, 17, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_21, 18, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_22, 19, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_23, 20, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_24, 21, ADC_SAMPLETIME_6_5);
        adc_ordinary_channel_set(ADC1, ADC_CHANNEL_25, 23, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_26, 23, ADC_SAMPLETIME_6_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_27, 24, ADC_SAMPLETIME_6_5);

  /* config ordinary trigger source and trigger edge */
  adc_ordinary_conversion_trigger_set(ADC1, ADC_ORDINARY_TRIG_TMR1CH1, ADC_ORDINARY_TRIG_EDGE_NONE);

  /* config dma mode,it's not useful when common dma mode is use */
  adc_dma_mode_enable(ADC1, TRUE);

  /* config dma request repeat,it's not useful when common dma mode is use */
  adc_dma_request_repeat_enable(ADC1, TRUE);

  /* enable adc overflow interrupt */
  adc_interrupt_enable(ADC1, ADC_OCCO_INT, TRUE);

  /* set oversampling ratio and shift */
  adc_oversample_ratio_shift_set(ADC1, ADC_OVERSAMPLE_RATIO_8, ADC_OVERSAMPLE_SHIFT_3);
  /* disable ordinary oversampling trigger mode */
  adc_ordinary_oversample_trig_enable(ADC1, FALSE);
  /* set ordinary oversample restart mode */
  adc_ordinary_oversample_restart_set(ADC1, ADC_OVERSAMPLE_CONTINUE);
  /* enable ordinary oversampling */
  adc_ordinary_oversample_enable(ADC1, TRUE);



  /* adc enable */
  adc_enable(ADC1, TRUE);
  while(adc_flag_get(ADC1, ADC_RDY_FLAG) == RESET);

  /* adc calibration */
  adc_calibration_init(ADC1);
  while(adc_calibration_init_status_get(ADC1));
  adc_calibration_start(ADC1);
  while(adc_calibration_status_get(ADC1));
}

参照ADC的DMA程序,配置DMA的配置如下:
/**
  * @brief  dma configuration.
  * @param  none
  * @retval none
  */
static void dma_config(void)
{
  dma_init_type dma_init_struct;
  crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
  nvic_irq_enable(DMA1_Channel1_IRQn, 0, 0);

  dma_reset(DMA1_CHANNEL1);
  dma_default_para_init(&dma_init_struct);
  dma_init_struct.buffer_size = 120;
  dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
  dma_init_struct.memory_base_addr = (uint32_t)adc1_ordinary_valuetab;
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)&(ADC1->odt);
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_HIGH;
  dma_init_struct.loop_mode_enable = TRUE;
  dma_init(DMA1_CHANNEL1, &dma_init_struct);

  dmamux_enable(DMA1, TRUE);
  dmamux_init(DMA1MUX_CHANNEL1, DMAMUX_DMAREQ_ID_ADC1);

  /* enable dma transfer complete interrupt */
  dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);
  dma_channel_enable(DMA1_CHANNEL1, TRUE);
}

移植TFT的显示程序(略)
在主程序中添加数据转换的命令与数据读取、取平值数,并做显示,附源代码:
int main(void)
{
  __IO uint32_t index = 0;
        uint8_t j = 0;
        uint32_t val[24]={0};
        uint8_t show_str[20] = {0};
  nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);

  /* config the system clock */
  system_clock_config();

  /* init at start board */
  at32_board_init();
  at32_led_off(LED2);
  at32_led_off(LED3);
  at32_led_off(LED4);
  uart_print_init(115200);
  gpio_config();
  dma_config();
  adc_config();
  printf("tmr_trigger_automatic_preempt \r\n");


  at32_led_on(LED2);
        IVO28_GC9306_P4();
        LCD_Fill(0,0,LCD_W,LCD_H,WHITE);

  //LCD_ShowString(10,10, "HELLO AT32F423",BLUE,WHITE,32,0);
        LCD_ShowChinese(20,6,"电压监测系统",BLUE,WHITE,32,0);//显示汉字串
  while(1)
  {
                 /* adc1 software trigger start conversion */
  for(index = 0; index < 5; index++)
  {
    adc_ordinary_software_trigger_enable(ADC1, TRUE);
               
    delay_ms(100);
  }
  if((dma_trans_complete_flag == 0) || (adc1_overflow_flag != 0))
  {
    /* printf flag when error occur */
    at32_led_on(LED3);
    at32_led_on(LED4);
    printf("error occur\r\n");
    printf("adc1_overflow_flag = %d\r\n",adc1_overflow_flag);
    printf("dma_trans_complete_flag = %d\r\n",dma_trans_complete_flag);
  }
  else
  {
    /* printf data when conversion end without error */
    printf("conversion end without error\r\n");
    for(index = 0; index < 5; index++)
    {
                        for(j=0;j<24;j++)
                        {
                                val[j] += adc1_ordinary_valuetab[index][j];
                        }
    }
  }
        for(j=0;j<8; j++)
        {
                sprintf(show_str,"%02d:%1.3f",j+1,((float)(val[j]/5) * 3.32)/4095);       
                LCD_ShowString(0,60+j*32, show_str,BLUE,WHITE,16,0);
                val[j] = 0;       
        }
        for(j=8;j<16; j++)
        {
                sprintf(show_str,"%02d:%1.3f",j+1,((float)(val[j]/5) * 3.32)/4095);       
                LCD_ShowString(80,60+(j-8)*32, show_str,BLUE,WHITE,16,0);
                val[j] = 0;       
        }
        for(j=16;j<24; j++)
        {
                sprintf(show_str,"%02d:%1.3f",j+1,((float)(val[j]/5) * 3.32)/4095);       
                LCD_ShowString(160,60+(j-16)*32, show_str,BLUE,WHITE,16,0);
                val[j] = 0;       
        }
        dma_trans_complete_flag = 0;
  }
}

【工程实验效果】
在LCD屏中,按1-24路通道显示电压如下:

用福禄克18B+与之对照,发现电压差距在1MV左右,精度非常的高,对电压的反馈也非常之好。
【讨论】
这个项目主要用到的AT32F423的24路模拟ADC输入,采用DMA的数据传输。同时驱动了TFTLCD屏。把24路电压的测量结果全部展现在显示屏上。同时与福禄克18B+的万用表进行对比测量,误差在正负1mV左右,各路之间的电压差也在正负3mV左右。
此次是用开发板进行了工程的验证,因为采用的参考电压是普通的数字电源,如果用到实际的工程中,需要使用精度高的稳压参考电源来进行测量,这样精度更加好。
在实际的工程中可以引入电压监的范围,来设定显示的颜色,超过规定的范围可以用警告来提示用户,同时可以把数据存到SD卡中,或者是经过UART等外设把数据上传到上位机中供用户进行数据挖掘等。
总之,对AT32F423的多路ADC进行测试,发现达到12位ADC的测量预期效果。如果结合放大器等辅助,可以实现高精的电压、电流测量。
【附工程视频介绍】


使用特权

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

本版积分规则

156

主题

748

帖子

10

粉丝