AT32 ADC — 定时器触发自动抢占

AT32 AT32F403A_407_Firmware_Library V2.2.2 ADC 定时器触发自动抢占 示例教学

ADC — 定时器触发自动抢占

固件库: AT32F403A_407_Firmware_Library V2.2.2
芯片: AT32
源文件: AT32/AT32F403A_407_Firmware_Library_V2.2.2/project/at_start_f403a/examples/adc/tmr_trigger_automatic_preempted/src/main.c


功能简介

本示例演示 ADC 模数转换器的基本用法。ADC 将模拟电压信号转换为数字值,是读取传感器数据的关键外设。

硬件准备

  • 电位器(可调电阻)连接到 ADC 输入引脚
  • 或使用内部温度传感器(无需额外硬件)

完整代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include "at32f403a_407_board.h"
#include "at32f403a_407_clock.h"
__IO uint16_t adc1_ordinary_valuetab[3] = {0};
__IO uint16_t adc1_preempt_valuetab[3] = {0};
__IO uint16_t dma_trans_complete_flag = 0;
__IO uint16_t ordinary_conversion_times_index = 0;
__IO uint16_t preempt_conversion_count = 0;
__IO uint16_t preempt_conversion_times_index = 0;

static void gpio_config(void)
{
  gpio_init_type gpio_initstructure;
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);

  gpio_default_para_init(&gpio_initstructure);
  gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG;
  gpio_initstructure.gpio_pins = GPIO_PINS_4 | GPIO_PINS_5 | GPIO_PINS_6 | GPIO_PINS_7;
  gpio_init(GPIOA, &gpio_initstructure);

  gpio_initstructure.gpio_mode = GPIO_MODE_ANALOG;
  gpio_initstructure.gpio_pins = GPIO_PINS_0 | GPIO_PINS_1;
  gpio_init(GPIOB, &gpio_initstructure);
}

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 = 3;
  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);

  dma_interrupt_enable(DMA1_CHANNEL1, DMA_FDT_INT, TRUE);
}

static void tmr1_config(void)
{
  gpio_init_type gpio_initstructure;
  tmr_output_config_type tmr_oc_init_structure;
  crm_clocks_freq_type crm_clocks_freq_struct = {0};
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);

  gpio_default_para_init(&gpio_initstructure);
  gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
  gpio_initstructure.gpio_pins = GPIO_PINS_8;
  gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
  gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
  gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init(GPIOA, &gpio_initstructure);

  /* get system clock */
  crm_clocks_freq_get(&crm_clocks_freq_struct);

  crm_periph_clock_enable(CRM_TMR1_PERIPH_CLOCK, TRUE);

  /* (systemclock/(systemclock/10000))/10000 = 1Hz(1s) */
  tmr_base_init(TMR1, 9999, (crm_clocks_freq_struct.sclk_freq/10000 - 1));
  tmr_cnt_dir_set(TMR1, TMR_COUNT_UP);
  tmr_clock_source_div_set(TMR1, TMR_CLOCK_DIV1);

  tmr_output_default_para_init(&tmr_oc_init_structure);
  tmr_oc_init_structure.oc_mode = TMR_OUTPUT_CONTROL_PWM_MODE_A;
  tmr_oc_init_structure.oc_polarity = TMR_OUTPUT_ACTIVE_LOW;
  tmr_oc_init_structure.oc_output_state = TRUE;
  tmr_oc_init_structure.oc_idle_state = FALSE;
  tmr_output_channel_config(TMR1, TMR_SELECT_CHANNEL_1, &tmr_oc_init_structure);
  tmr_channel_value_set(TMR1, TMR_SELECT_CHANNEL_1, 5000);
}

static void adc_config(void)
{
  adc_base_config_type adc_base_struct;
  crm_periph_clock_enable(CRM_ADC1_PERIPH_CLOCK, TRUE);
  adc_reset(ADC1);
  crm_adc_clock_div_set(CRM_ADC_DIV_4);
  nvic_irq_enable(ADC1_2_IRQn, 0, 0);
  adc_base_default_para_init(&adc_base_struct);

  /* select combine mode */
  adc_combine_mode_select(ADC_INDEPENDENT_MODE);

  /* ADC1 config */
  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 = 3;
  adc_base_config(ADC1, &adc_base_struct);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_4, 1, ADC_SAMPLETIME_239_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_5, 2, ADC_SAMPLETIME_239_5);
  adc_ordinary_channel_set(ADC1, ADC_CHANNEL_6, 3, ADC_SAMPLETIME_239_5);
  adc_ordinary_conversion_trigger_set(ADC1, ADC12_ORDINARY_TRIG_TMR1CH1, TRUE);
  adc_dma_mode_enable(ADC1, TRUE);

  adc_preempt_channel_length_set(ADC1, 3);
  adc_preempt_channel_set(ADC1, ADC_CHANNEL_7, 1, ADC_SAMPLETIME_239_5);
  adc_preempt_channel_set(ADC1, ADC_CHANNEL_8, 2, ADC_SAMPLETIME_239_5);
  adc_preempt_channel_set(ADC1, ADC_CHANNEL_9, 3, ADC_SAMPLETIME_239_5);
  adc_preempt_conversion_trigger_set(ADC1, ADC12_PREEMPT_TRIG_SOFTWARE, TRUE);
  adc_preempt_auto_mode_enable(ADC1, TRUE);
  adc_interrupt_enable(ADC1, ADC_PCCE_INT, TRUE);

  adc_enable(ADC1, TRUE);

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

int main(void)
{
  nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  system_clock_config();
  at32_board_init();
  at32_led_off(LED2);
  at32_led_off(LED3);
  at32_led_off(LED4);
  uart_print_init(115200);
  gpio_config();
  tmr1_config();
  dma_config();
  adc_config();

  /* enable DMA after ADC activation */
  dma_channel_enable(DMA1_CHANNEL1, TRUE);

  printf("tmr_trigger_automatic_preempted \r\n");
  tmr_counter_enable(TMR1, TRUE);
  tmr_channel_enable(TMR1, TMR_SELECT_CHANNEL_1, TRUE);
  tmr_output_enable(TMR1, TRUE);
  while(1)
  {
    /* wait ordinary conversion end */
    if(ordinary_conversion_times_index != dma_trans_complete_flag)
    {
      ordinary_conversion_times_index = dma_trans_complete_flag;
      printf("ordinary_conversion_times_index = %d\r\n",ordinary_conversion_times_index);
      printf("adc1_ordinary_valuetab[0] = 0x%x\r\n", adc1_ordinary_valuetab[0]);
      printf("adc1_ordinary_valuetab[1] = 0x%x\r\n", adc1_ordinary_valuetab[1]);
      printf("adc1_ordinary_valuetab[2] = 0x%x\r\n", adc1_ordinary_valuetab[2]);
      printf("\r\n");
      at32_led_toggle(LED2);
    }

    /* wait preempt conversion end */
    if(preempt_conversion_times_index != preempt_conversion_count)
    {
      preempt_conversion_times_index = preempt_conversion_count;
      printf("preempt_conversion_times_index = %d\r\n",preempt_conversion_times_index);
      printf("adc1_preempt_valuetab[0] = 0x%x\r\n", adc1_preempt_valuetab[0]);
      printf("adc1_preempt_valuetab[1] = 0x%x\r\n", adc1_preempt_valuetab[1]);
      printf("adc1_preempt_valuetab[2] = 0x%x\r\n", adc1_preempt_valuetab[2]);
      printf("\r\n");
      at32_led_toggle(LED3);
    }
  }
}

代码讲解

系统时钟初始化:配置 MCU 的主频和时钟树。不同芯片的时钟配置不同,一般由工具生成。

板级初始化:初始化开发板上的 LED、按键等基础外设。

串口初始化:配置串口波特率(这里是 115200),使能 printf 重定向。

printf 输出:通过串口打印调试信息。需要先调用 uart_print_init 完成重定向。

GPIO 配置:设置引脚为输出/输入模式,选择推挽/开漏输出,配置上拉/下拉。

ADC 配置:选择采样通道、采样时间、触发方式、数据对齐方式。采样时间越长精度越高。

DMA 配置:设置源地址、目标地址、传输方向、数据宽度。DMA 搬运数据不占用 CPU。

定时器配置:设置预分频器(PSC)和重装载值(ARR),定时时间 = (PSC+1) × (ARR+1) / 时钟频率。

PWM 配置:在定时器基础上设置比较值(CCR),占空比 = CCR / (ARR+1)。CCR 越大占空比越高。

中断优先级配置:NVIC 设置中断的抢占优先级和子优先级。数字越小优先级越高。

校准:ADC 使用前必须校准,消除工艺偏差。校准期间不要操作 ADC。

实验现象

  • 串口助手打印 ADC 采样值
  • 转动电位器时数值变化
  • 温度传感器示例会显示芯片温度

注意事项

  • ADC 参考电压影响测量精度
  • 采样时间要根据信号源阻抗选择
  • 使用前务必校准(Calibration)
世界是你们
使用 Hugo 构建
主题 StackJimmy 设计