#申请原创# #有奖活动#[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的测量预期效果。如果结合放大器等辅助,可以实现高精的电压、电流测量。
【附工程视频介绍】
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
|