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

[复制链接]
 楼主| lulugl 发表于 2023-11-7 20:43 | 显示全部楼层 |阅读模式
#申请原创# #有奖活动#[url=home.php?mod=space&uid=760190]@21小跑堂 [/url]
【项目简介】
在工业冶具行业中,经常会有多路电压测试的需求,如下图所示:
95e91cfbeff15a34ee535591bf15fe30
这种解决方案,虽然适配简单,但是如果对其进行人工判断,一来观测的人容易产生误差,而且需要人工值守,成本也比较高。
而用AT32F423的ADC,可以实现一个单片机对应测量24路电压。并且可以通过设定正常电压范围,就可以实现智能的电压监测,对于异常情况可以通过多种途径进行报警提示,后台数据记录等。
【AT32F423的ADC简介】
在数据手册《DS_AT32F423_V2.00_CH.pdf》中如下图所描述:
c85346f47b9350d35685bed3e6d87304
AT32F423中有一个ADC1,总共26个通道,ADC_IN0 至 ADC_IN15 为外部模拟输入,ADC_IN16 为内部温度传感器,ADC_IN17 为内部参考电压,ADC_IN20 至 ADC_IN27 为外部模拟输入。这样就有24个通道可以做输入检测。
【程序的实现步骤】
使用雅特力的图形化配置工具Work Bench对24个通的ADC进行图形化配置:
b3d3d7a4a6c324f84fc85a24058951ea
在代码预览中复制ADC的配置代码到工程模板中的adc的gpio初始化函数中:
7ab288abbe6e286e09a1fb52f32567a6
41cb0fe2875b4298602a85de934a4ccb
参照adc的DMA例程中添加ADC的配置,具体程序如下:
  1. /**
  2.   * [url=home.php?mod=space&uid=247401]@brief[/url]  adc configuration.
  3.   * @param  none
  4.   * @retval none
  5.   */
  6. static void adc_config(void)
  7. {
  8.   adc_common_config_type adc_common_struct;
  9.   adc_base_config_type adc_base_struct;
  10.   crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);
  11.   nvic_irq_enable(ADC1_IRQn, 0, 0);
  12.   crm_adc_clock_select(CRM_ADC_CLOCK_SOURCE_HCLK);

  13.   adc_common_default_para_init(&adc_common_struct);

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

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

  18.   adc_common_config(&adc_common_struct);

  19.   adc_base_default_para_init(&adc_base_struct);

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

  26.   /* config ordinary channel */
  27.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_6_5);
  28.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_6_5);
  29.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_6_5);
  30.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_3, 4, ADC_SAMPLETIME_6_5);
  31.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_4, 5, ADC_SAMPLETIME_6_5);
  32.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_5, 6, ADC_SAMPLETIME_6_5);
  33.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_6, 7, ADC_SAMPLETIME_6_5);
  34.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_7, 8, ADC_SAMPLETIME_6_5);
  35.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_8, 9, ADC_SAMPLETIME_6_5);
  36.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_9, 10, ADC_SAMPLETIME_6_5);
  37.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_10, 11, ADC_SAMPLETIME_6_5);
  38.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_11, 12, ADC_SAMPLETIME_6_5);
  39.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_12, 13, ADC_SAMPLETIME_6_5);
  40.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_13, 14, ADC_SAMPLETIME_6_5);
  41.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_14, 15, ADC_SAMPLETIME_6_5);
  42.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_15, 16, ADC_SAMPLETIME_6_5);
  43.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_20, 17, ADC_SAMPLETIME_6_5);
  44.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_21, 18, ADC_SAMPLETIME_6_5);
  45.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_22, 19, ADC_SAMPLETIME_6_5);
  46.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_23, 20, ADC_SAMPLETIME_6_5);
  47.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_24, 21, ADC_SAMPLETIME_6_5);
  48.         adc_ordinary_channel_set(ADC1, ADC_CHANNEL_25, 23, ADC_SAMPLETIME_6_5);
  49.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_26, 23, ADC_SAMPLETIME_6_5);
  50.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_27, 24, ADC_SAMPLETIME_6_5);

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

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

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

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

  59.   /* set oversampling ratio and shift */
  60.   adc_oversample_ratio_shift_set(ADC1, ADC_OVERSAMPLE_RATIO_8, ADC_OVERSAMPLE_SHIFT_3);
  61.   /* disable ordinary oversampling trigger mode */
  62.   adc_ordinary_oversample_trig_enable(ADC1, FALSE);
  63.   /* set ordinary oversample restart mode */
  64.   adc_ordinary_oversample_restart_set(ADC1, ADC_OVERSAMPLE_CONTINUE);
  65.   /* enable ordinary oversampling */
  66.   adc_ordinary_oversample_enable(ADC1, TRUE);



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

  70.   /* adc calibration */
  71.   adc_calibration_init(ADC1);
  72.   while(adc_calibration_init_status_get(ADC1));
  73.   adc_calibration_start(ADC1);
  74.   while(adc_calibration_status_get(ADC1));
  75. }

参照ADC的DMA程序,配置DMA的配置如下:
  1. /**
  2.   * @brief  dma configuration.
  3.   * @param  none
  4.   * @retval none
  5.   */
  6. static void dma_config(void)
  7. {
  8.   dma_init_type dma_init_struct;
  9.   crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
  10.   nvic_irq_enable(DMA1_Channel1_IRQn, 0, 0);

  11.   dma_reset(DMA1_CHANNEL1);
  12.   dma_default_para_init(&dma_init_struct);
  13.   dma_init_struct.buffer_size = 120;
  14.   dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
  15.   dma_init_struct.memory_base_addr = (uint32_t)adc1_ordinary_valuetab;
  16.   dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
  17.   dma_init_struct.memory_inc_enable = TRUE;
  18.   dma_init_struct.peripheral_base_addr = (uint32_t)&(ADC1->odt);
  19.   dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
  20.   dma_init_struct.peripheral_inc_enable = FALSE;
  21.   dma_init_struct.priority = DMA_PRIORITY_HIGH;
  22.   dma_init_struct.loop_mode_enable = TRUE;
  23.   dma_init(DMA1_CHANNEL1, &dma_init_struct);

  24.   dmamux_enable(DMA1, TRUE);
  25.   dmamux_init(DMA1MUX_CHANNEL1, DMAMUX_DMAREQ_ID_ADC1);

  26.   /* enable dma transfer complete interrupt */
  27.   dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);
  28.   dma_channel_enable(DMA1_CHANNEL1, TRUE);
  29. }

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

  8.   /* config the system clock */
  9.   system_clock_config();

  10.   /* init at start board */
  11.   at32_board_init();
  12.   at32_led_off(LED2);
  13.   at32_led_off(LED3);
  14.   at32_led_off(LED4);
  15.   uart_print_init(115200);
  16.   gpio_config();
  17.   dma_config();
  18.   adc_config();
  19.   printf("tmr_trigger_automatic_preempt \r\n");


  20.   at32_led_on(LED2);
  21.         IVO28_GC9306_P4();
  22.         LCD_Fill(0,0,LCD_W,LCD_H,WHITE);

  23.   //LCD_ShowString(10,10, "HELLO AT32F423",BLUE,WHITE,32,0);
  24.         LCD_ShowChinese(20,6,"电压监测系统",BLUE,WHITE,32,0);//显示汉字串
  25.   while(1)
  26.   {
  27.                  /* adc1 software trigger start conversion */
  28.   for(index = 0; index < 5; index++)
  29.   {
  30.     adc_ordinary_software_trigger_enable(ADC1, TRUE);
  31.                
  32.     delay_ms(100);
  33.   }
  34.   if((dma_trans_complete_flag == 0) || (adc1_overflow_flag != 0))
  35.   {
  36.     /* printf flag when error occur */
  37.     at32_led_on(LED3);
  38.     at32_led_on(LED4);
  39.     printf("error occur\r\n");
  40.     printf("adc1_overflow_flag = %d\r\n",adc1_overflow_flag);
  41.     printf("dma_trans_complete_flag = %d\r\n",dma_trans_complete_flag);
  42.   }
  43.   else
  44.   {
  45.     /* printf data when conversion end without error */
  46.     printf("conversion end without error\r\n");
  47.     for(index = 0; index < 5; index++)
  48.     {
  49.                         for(j=0;j<24;j++)
  50.                         {
  51.                                 val[j] += adc1_ordinary_valuetab[index][j];
  52.                         }
  53.     }
  54.   }
  55.         for(j=0;j<8; j++)
  56.         {
  57.                 sprintf(show_str,"%02d:%1.3f",j+1,((float)(val[j]/5) * 3.32)/4095);       
  58.                 LCD_ShowString(0,60+j*32, show_str,BLUE,WHITE,16,0);
  59.                 val[j] = 0;       
  60.         }
  61.         for(j=8;j<16; j++)
  62.         {
  63.                 sprintf(show_str,"%02d:%1.3f",j+1,((float)(val[j]/5) * 3.32)/4095);       
  64.                 LCD_ShowString(80,60+(j-8)*32, show_str,BLUE,WHITE,16,0);
  65.                 val[j] = 0;       
  66.         }
  67.         for(j=16;j<24; j++)
  68.         {
  69.                 sprintf(show_str,"%02d:%1.3f",j+1,((float)(val[j]/5) * 3.32)/4095);       
  70.                 LCD_ShowString(160,60+(j-16)*32, show_str,BLUE,WHITE,16,0);
  71.                 val[j] = 0;       
  72.         }
  73.         dma_trans_complete_flag = 0;
  74.   }
  75. }

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

您需要登录后才可以回帖 登录 | 注册

本版积分规则

180

主题

830

帖子

12

粉丝
快速回复 返回顶部 返回列表