OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 343|回复: 0

在AT32微控制器上通过同一外部触发源触发3个ADC转换的方法

[复制链接]

55

主题

55

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
223
金钱
223
注册时间
2024-12-9
在线时间
12 小时
发表于 2026-8-12 14:01:28 | 显示全部楼层 |阅读模式
应用笔记
在AT32微控制器上通过同一外部触发源触发3个ADC转换的方法
前言
本应用笔记介绍了在AT32微控制器上通过同一外部触发源触发3个ADC转换的方法,实现在任意时刻3个ADC通道的同步动作,以此来满足需要3个ADC同步转换需求的应用。
注:本应用笔记对应的代码是基于雅特力提供V2.x.x 板级支持包(BSP)而开发,对于其他版本BSP,需要注意使用上的区别。
支持型号列表:AT32F403系列,AT32F403A系列,AT32F407系列,AT32A403A系列

说明:本文档仅供有需求的小伙伴们参考,本文档中所包含的代码等资料,可在雅特力官网获取https://www.arterytek.com/cn/support/index.jsp?index=1


1                 ADC框架介绍
ADC 是一个将模拟输入信号转换为12 位数字信号的外设。采样率最高可达2MSPS。多达18 个通道源(16个外部和2个内部)可进行采样及转换。其具备如下功能:
1)支持包括单次、反复、序列、抢占自动转换或分割等多种转换模式选择;
2)普通通道和抢占通道均支持软件触发及外部触发,外部触发具备多种触发源选择;
2)转换数据可以设定左或右对齐方式进行存储,且抢占通道支持数据偏移量的设定;
3)电压监测特性允许应用程序监测输入电压是否超出用户定义的高/低阈值;
4)抢占通道组转换结束、通道转换结束、电压监测超出范围均具备响应中断的能力;
5)ADC时钟可调(时钟源来自PCLK2,ACCLK最大不得超过28MHz);
6)支持多达20个通道的采样转换(普通组16,抢占组4)、采样周期可根据需求调整;
7)普通通道转换数据可经DMA传输,当普通通道组设定多通道时,必须使用DMA获取转换数据;
8)支持联动ADC1和ADC2的多种主从组合模式。
单个ADC框图如下:

2                 3ADC同步外部触发原理说明
ADC主从模式的普通同时模式下,ADC2会完全同步ADC1动作。ADC1与ADC3具备多种相同的触发源。基于此点,应用可将ADC1和ADC2组合成主从模式(普通同时模式),ADC3与ADC1选用相同的触发源,从而就可以达到3个ADC完全同步动作的效果。
应用实例中,将ADC1、ADC2、ADC3分别各初始化两个通道,ADC1使用通道4、5,ADC2使用通道7、8,ADC3使用通道10、11。故将实现如下的同步触发转换效果:
表1. 触发情景
触发
第一次 第一次 第一次
ADC1
通道4 通道5 通道4 通道5 通道4 通道5
ADC2
通道7 通道8 通道7 通道8 通道7 通道8
ADC3
通道10 通道11 通道10 通道11 通道10 通道11

     
        
   
   
   
触发方案
本例中ADC3与ADC1普通通道组均选用TMR1TRGOUT触发。
TMR1次定时器
-       配置“次定时器输入选择”为“滤波输入1”
-       配置“次定时器模式选择”为“TRGIN输入上升沿时,产生TMR使能事件”
由此每次发生在PA8口上的上升沿就会让TMR1产生使能事件。
TMR1主定时器
-       配置“主定时器输出信号选择”为“使能”
-       配置开启“单脉冲”模式
则每次TMR1使能时就会输出TRGOUT事件给ADC以触发转换。
在此配置基础上,即可实现三个ADC同步触发的效果,即:
每次发生在PA8口上的上升沿,会同步触发ADC1、ADC2、ADC3同时开始转换。
3                 ADC配置步骤
n  GPIO配置
例程配置代码如下
  1. static void gpio_config (void)
  2. {
  3.   gpio_init_type gpio_initstructure;
  4.   crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  5.   crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
  6.   crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);

  7.   gpio_default_para_init(&gpio_initstructure);
  8.   gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG;
  9.   gpio_initstructure.gpio_pins = GPIO_PINS_4 | GPIO_PINS_5 | GPIO_PINS_7;
  10.   gpio_init(GPIOA, &gpio_initstructure);
  11.   
  12.   gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG;
  13.   gpio_initstructure.gpio_pins = GPIO_PINS_0;
  14.   gpio_init(GPIOB, &gpio_initstructure);
  15.   
  16.   gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG;
  17.   gpio_initstructure.gpio_pins = GPIO_PINS_0 | GPIO_PINS_1;
  18.   gpio_init(GPIOC, &gpio_initstructure);
  19. }
复制代码

n  触发源配置
例程配置代码如下
  1. static void tmr1_config(void)
  2. {
  3.   gpio_init_type gpio_initstructure;
  4.   tmr_input_config_type  tmr_input_config_struct;
  5.   crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  6.   crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);
  7.   
  8.   gpio_default_para_init(&gpio_initstructure);
  9.   gpio_initstructure.gpio_mode = GPIO_MODE_INPUT;
  10.   gpio_initstructure.gpio_pins = GPIO_PINS_8;
  11.   gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  12.   gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
  13.   gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  14.   gpio_init(GPIOA, &gpio_initstructure);
  15.   tmr_reset(TMR1);
  16.   tmr_base_init(TMR1, 1, 0);
  17.   tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);
  18.   tmr_clock_source_div_set(TMR1, TMR_CLOCK_DIV1);
  19.   
  20.   /* configure tmr1 channel1 pin signal(rising edge) to generate enable event */
  21.   tmr_input_config_struct.input_channel_select = TMR_SELECT_CHANNEL_1;
  22.   tmr_input_config_struct.input_mapped_select = TMR_CC_CHANNEL_MAPPED_DIRECT;
  23.   tmr_input_config_struct.input_polarity_select = TMR_INPUT_RISING_EDGE;
  24.   tmr_input_channel_init(TMR3, &tmr_input_config_struct, TMR_CHANNEL_INPUT_DIV_1);
  25.   tmr_trigger_input_select(TMR1, TMR_SUB_INPUT_SEL_C1DF1);
  26.   tmr_sub_mode_select(TMR1, TMR_SUB_TRIGGER_MODE);
  27.   
  28.   /* configure tmr1 master output selection */
  29.   tmr_primary_mode_select(TMR1, TMR_PRIMARY_SEL_ENABLE);
  30.   
  31.   /* one cycle mode enable */
  32.   tmr_one_cycle_mode_enable(TMR1, TRUE);
  33. }
复制代码
例程使用TMR1的trgout事件触发ADC,应用设计需注意如下内容:
1)     TMR1配置开启“单脉冲”模式;
2)     TMR1配置“次定时器输入选择”为“滤波输入1”,对应输入引脚为PA8(此引脚可根据应用需求修改为TMR1_CHx对应的其他引脚);
3)     TMR1配置“次定时器模式选择”为“TRGIN输入上升沿时,产生TMR使能事件”;
4)     TMR1配置“主定时器输出信号选择”为“使能”。
n  DMA配置
例程配置代码如下
  1. static void dma_config(void)
  2. {
  3.   dma_init_type dma_init_struct;
  4.   crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);
  5.   crm_periph_clock_enable(CRM_DMA2_PERIPH_CLOCK, TRUE);
  6.   nvic_irq_enable(DMA1_Channel1_IRQn, 1, 0);
  7.   nvic_irq_enable(DMA2_Channel4_5_IRQn, 0, 0);
  8.   dma_reset(DMA1_CHANNEL1);
  9.   dma_reset(DMA2_CHANNEL5);
  10.   dma_default_para_init(&dma_init_struct);
  11.   dma_init_struct.buffer_size = 2;
  12.   dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;
  13.   dma_init_struct.memory_base_addr = (uint32_t)adc1_ordinary_valuetab;
  14.   dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_WORD;
  15.   dma_init_struct.memory_inc_enable = TRUE;
  16.   dma_init_struct.peripheral_base_addr = (uint32_t)&(ADC1->odt);
  17.   dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_WORD;
  18.   dma_init_struct.peripheral_inc_enable = FALSE;
  19.   dma_init_struct.priority = DMA_PRIORITY_HIGH;
  20.   dma_init_struct.loop_mode_enable = TRUE;
  21.   dma_init(DMA1_CHANNEL1, &dma_init_struct);

  22.   dma_init_struct.memory_base_addr = (uint32_t)adc3_ordinary_valuetab;
  23.   dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_HALFWORD;
  24.   dma_init_struct.peripheral_base_addr = (uint32_t)&(ADC3->odt);
  25.   dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_HALFWORD;
  26.   dma_init(DMA2_CHANNEL5, &dma_init_struct);

  27.   dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);
  28.   dma_interrupt_enable(DMA2_CHANNEL5, DMA_FDT_INT, TRUE);
  29.   dma_channel_enable(DMA1_CHANNEL1, TRUE);
  30.   dma_channel_enable(DMA2_CHANNEL5, TRUE);
  31. }
复制代码
例程中ADC1&ADC2的转换数据通过DMA1_CHANNEL1传输,ADC3的转换数据通过DMA2_CHANNEL5传输。对DMA配置需要注意如下内容:
1)     应用若需更换使用DMA_CHANNEL,需注意对通道进行弹性映射配置;
2)     ADC1&ADC2转换数据会组合成32位进行传输,因此用于其数据传输的DMA1_CHANNEL1的外设及存储器数据宽度必须配置为32 bit 位宽;
3)     ADC3的转换数据保持16位宽度,因此用于其数据传输的DMA2_CHANNEL5的外设及存储器数据宽度需配置为16 bit 位宽;
4)     数据传输是从ADC外设到存储器,因此DMA数据传输方向需设定为外设为源;
5)     为保障数据的稳定传输,DMA的通道传输数量通常匹配ADC的普通通道组个数设定;
6)     DMA通道优先级需结合应用设定,当ADC转换足够快时,为避免数据丢失传输,需适当提高DMA通道优先级。
n  ADC配置
例程配置代码如下
  1. static void adc_config(void)
  2. {
  3.   adc_base_config_type adc_base_struct;
  4.   crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);
  5.   crm_periph_clock_enable(CRM_ADC2_PERIPH_CLOCK, TRUE);
  6.   crm_periph_clock_enable(CRM_ADC3_PERIPH_CLOCK, TRUE);
  7.   crm_adc_clock_div_set(CRM_ADC_DIV_6);

  8.   /* select combine mode */
  9.   adc_combine_mode_select(ADC_ORDINARY_SMLT_ONLY_MODE);
  10.   adc_base_default_para_init(&adc_base_struct);
  11.   adc_base_struct.sequence_mode = TRUE;
  12.   adc_base_struct.repeat_mode = FALSE;
  13.   adc_base_struct.data_align = ADC_RIGHT_ALIGNMENT;
  14.   adc_base_struct.ordinary_channel_length = 2;
  15.   adc_base_config(ADC1, &adc_base_struct);
  16.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_4, 1, ADC_SAMPLETIME_239_5);
  17.   adc_ordinary_channel_set(ADC1, ADC_CHANNEL_5, 2, ADC_SAMPLETIME_239_5);
  18.   adc_ordinary_conversion_trigger_set(ADC1, ADC12_ORDINARY_TRIG_TMR1TRGOUT, TRUE);
  19.   adc_dma_mode_enable(ADC1, TRUE);

  20.   adc_base_config(ADC2, &adc_base_struct);
  21.   adc_ordinary_channel_set(ADC2, ADC_CHANNEL_7, 1, ADC_SAMPLETIME_239_5);
  22.   adc_ordinary_channel_set(ADC2, ADC_CHANNEL_8, 2, ADC_SAMPLETIME_239_5);
  23.   adc_ordinary_conversion_trigger_set(ADC2, ADC12_ORDINARY_TRIG_SOFTWARE, TRUE);

  24.   adc_base_config(ADC3, &adc_base_struct);
  25.   adc_ordinary_channel_set(ADC3, ADC_CHANNEL_10, 1, ADC_SAMPLETIME_239_5);
  26.   adc_ordinary_channel_set(ADC3, ADC_CHANNEL_11, 2, ADC_SAMPLETIME_239_5);
  27.   adc_ordinary_conversion_trigger_set(ADC3, ADC3_ORDINARY_TRIG_TMR1TRGOUT, TRUE);
  28.   adc_dma_mode_enable(ADC3, TRUE);

  29.   adc_enable(ADC1, TRUE);
  30.   adc_enable(ADC2, TRUE);
  31.   adc_calibration_init(ADC1);
  32.   while(adc_calibration_init_status_get(ADC1));
  33.   adc_calibration_start(ADC1);
  34.   while(adc_calibration_status_get(ADC1));
  35.   adc_calibration_init(ADC2);
  36.   while(adc_calibration_init_status_get(ADC2));
  37.   adc_calibration_start(ADC2);
  38.   while(adc_calibration_status_get(ADC2));

  39.   adc_enable(ADC3, TRUE);
  40.   adc_calibration_init(ADC3);
  41.   while(adc_calibration_init_status_get(ADC3));
  42.   adc_calibration_start(ADC3);
  43.   while(adc_calibration_status_get(ADC3));
  44. }
复制代码
例程中有使用ADC1、ADC2和ADC3来实现通道转换。对ADC的配置需要注意如下内容:
1)     ADC2转换数据没有自己独立的DMA传输,并且为了实现同时转换,ADC1与ADC2必须组合成主从模式的普通同时模式。ADC3有自己独立的DMA请求,故ADC3配置为独立模式;
2)     为了实现同时转换,ADC1与ADC3普通通道需选择相同触发源,本例选用TMR1_TRGOUT,其中还存在如下一些相同触发源
-      TMR1_CH1 event
-      TMR1_CH3 event
-      TMR8_CH1 event
-      TMR8_TRGOUT event
3)     为避免作为从模式的ADC2丢失同步规则,ADC2的触发源必须选择为软件触发;
4)     为保障触发能被有效相应,触发间隔不能小于ADC序列转换时间;
5)     主从模式的ADC1和ADC2,校准必须在两个ADC使能后进行;
6)     需要确保同一通道不可同时被多个ADC采样转换;
7)     为保障数据的稳定传输,DMA的通道传输数量通常匹配ADC的普通通道组个数设定;
8)     本应用案例仅适用ADC普通通道,抢占通道转换数据不具备DMA传输能力。
n  整体初始化顺序建议
建议初始化顺序如下
  1. gpio_config();

  2. tmr1_config();

  3. dma_config();

  4. adc_config();
复制代码

如此初始化顺序建议,主要是基于如下合理性考虑:
1)     在ADC初始化使能之前初始化timer
因为timer初始化时为做同步而会产生一次非预期的触发事件,若此时ADC已初始化完毕,则会异常响应该触发事件;
2)     在ADC初始化使能前配置并使能DMA
因为ADC使能后,只要存在触发条件,ADC就会开始相应转换,每个通道转换完毕会立即产生DMA传输请求,若DMA是能点滞后,会存在数据传输请求不能被及时相应,导致丢失数据最终产生数据错位的现象。
n  中断服务函数设计
例程代码做如下设计
  1. void DMA1_Channel1_IRQHandler(void)
  2. {
  3.   if(dma_flag_get(DMA1_FDT1_FLAG) != RESET)
  4.   {
  5.     dma_flag_clear(DMA1_FDT1_FLAG);
  6.     dma1_trans_complete_flag++;
  7.     if(dma1_trans_complete_flag == 0xFFFFFFFF)
  8.     {
  9.       dma1_trans_complete_flag = 0;
  10.     }
  11.   }
  12. }
  13. void DMA2_Channel4_5_IRQHandler(void)
  14. {
  15.   if(dma_flag_get(DMA2_FDT5_FLAG) != RESET)
  16.   {
  17.     dma_flag_clear(DMA2_FDT5_FLAG);
  18.     dma2_trans_complete_flag++;
  19.     if(dma2_trans_complete_flag == 0xFFFFFFFF)
  20.     {
  21.       dma2_trans_complete_flag = 0;
  22.     }
  23.   }
  24. }
复制代码
例程只使用了两个DMA的传输完成中断,中断服务函数内只记录是否发生了传输完成事件。
中断服务函数设计以精简为主要原则,由于中断函数的响应具备优先原则,为避免因过于繁琐的中断响应而滞后其他较重要的应用代码执行,所以原则上不建议在中断函数内堆太多应用逻辑。
n  main函数设计
例程代码做如下设计
  1. int main(void)
  2. {
  3.   nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  4.   system_clock_config();
  5.   at32_board_init();
  6.   at32_led_off(LED2);
  7.   at32_led_off(LED3);
  8.   at32_led_off(LED4);
  9.   uart_print_init(115200);
  10.   gpio_config();
  11.   tmr1_config();
  12.   dma_config();
  13.   adc_config();
  14.   printf("3adc_external_simultaneous_trigger \r\n");
  15.   while(1)
  16.   {
  17.     if(dma1_trans_complete_flag != old_dma1_trans_complete_flag)
  18.     {
  19.       old_dma1_trans_complete_flag = dma1_trans_complete_flag;
  20.       printf("adc1_channel4_data[0] = 0x%x\r\n", adc1_ordinary_valuetab[0] & 0xFFFF);
  21.       printf("adc1_channel5_data[1] = 0x%x\r\n", adc1_ordinary_valuetab[1] & 0xFFFF);
  22.       printf("adc2_channel7_data[0] = 0x%x\r\n", (adc1_ordinary_valuetab[0] >> 16) & 0xFFFF);
  23.       printf("adc2_channel8_data[1] = 0x%x\r\n", (adc1_ordinary_valuetab[1] >> 16) & 0xFFFF);
  24.       printf("\r\n");
  25.       at32_led_toggle(LED2);
  26.     }
  27.     if(dma2_trans_complete_flag != old_dma2_trans_complete_flag)
  28.     {
  29.       old_dma2_trans_complete_flag = dma2_trans_complete_flag;
  30.       printf("adc3_channel10_data[0] = 0x%x\r\n", adc3_ordinary_valuetab[0]);
  31.       printf("adc3_channel11_data[1] = 0x%x\r\n", adc3_ordinary_valuetab[1]);
  32.       printf("\r\n");
  33.       at32_led_toggle(LED3);
  34.     }
  35.   }
  36. }
复制代码
例程main函数内,除外设初始化外,只进行了转换数据的打印输出,通过查询DMA传输完成事件标志来实现。应用设计需要注意如下内容:
配置为主从普通同时模式的ADC1和ADC2的数据,被硬件组合封装成了32位的数据,所以需要对透过DMA获取到的32位数据进行解析后才可使用,其中,高16位为ADC2的转换数据,低16位为ADC1的转换数据。
4                 Demo Code快速使用方法
n  硬件资源
AT-START-F403A V1.0 实验板:

ADC使用的通道与GPIO口对应如下表所示:
表2. 通道对应
ADC1 通道4à PA4 通道5à PA5
ADC2 通道7à PA7 通道8à PB0
ADC3 通道10à PC0 通道11à PC1

在做实验时,分别对这6个GPIO口灌入电压值即可
n  测试方法
1.     打开相应工程,编译并下载到目标板;
2.     对ADC123对应的管脚灌入电压值;
3.     在PA8口上输入脉冲,通过串口打印或进入调试模式查看转换结果是否符合预期。
结果如下图:



可见相应通道电压值已经转换并通过DMA传输到指定的数组中。
注:所有project都是基于AT32F403A而建立,若用户需要在其他型号上使用,请参考AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种型号示例工程进行简单修改即可。
注:所有project都是基于keil 5而建立,若用户需要在其他编译环境上使用,请参考
AT32xxx_Firmware_Library_V2.x.x\project\at_start_xxx\templates中各种编译环境(例如IAR6/7,keil 4/5)进行简单修改即可。








回复

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-8-22 21:02

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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