STM32
直播中

YYXIAO

9年用户 1238经验值
擅长:接口/总线/驱动
私信 关注
[问答]

怎样通过GD32F350去读取ADC通道扫描模式

怎样通过GD32F350去读取ADC通道扫描模式?并通过DMA传输出来呢?

回帖(1)

张丹

2021-11-8 14:44:27
  芯片:GD32F350 运行在8M
  目标:每10ms读取三个adc通道,并且通过DMA传输
  说明:本次使用ADC的定时扫描模式,由定时器触发ADC采集转换
  ADC扫描模式预先设定好读取的通道,比如ch4 ch5 ch8,当触发adc时候就会连续采集三个通道的数据,通过dma传输到内存,过程(触发-》4-》5-》8-》触发-》4-》5-》8-》触发-》4-》5-》8-》触发)
  注意是触发一次即转换三个通道的数据,而不是触发一次转换一个通道
  1.配置DMA传输
  //adc 的dma是否传输完成标志位
  bool flag_DMA_ADC_accomplish = FALSE;
  //dma缓冲区
  uint16_t battery_ADC_value_arry[ADC_VALUE_ARRY_SIZE] = {0};
  /* 使能dma时钟 */
  rcu_periph_clock_enable(RCU_DMA);
  /* dma初始化结构体 */
  dma_parameter_struct dma_data_parameter;
  /* 复位DAM通道 */
  dma_deinit(DMA_CH0);
  //ADC_RDATA是读取adc数据的寄存器
  dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA);
  dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
  //battery_ADC_value_arry 是存储adc数据的内存数组
  dma_data_parameter.memory_addr = (uint32_t)(&battery_ADC_value_arry);
  dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
  dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
  //从外设到内存
  dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
  //这里的150是设置dma搬运数据的个数即3个通道共读取150个数据,每个通道读取50个,完成后会触发中断
  dma_data_parameter.number = 150;
  dma_data_parameter.priority = DMA_PRIORITY_HIGH;
  dma_init(DMA_CH0, &dma_data_parameter);
  dma_circulation_enable(DMA_CH0);
  //设置dma中断优先级
  nvic_irq_enable(DMA_Channel0_IRQn, 0, 0);
  /* enable DMA transfer complete interrupt */
  dma_interrupt_enable(DMA_CH0, DMA_INT_FTF);
  /* 使能通道 */
  dma_channel_enable(DMA_CH0);
  2.配置ADC模式和通道
  #define ADC_DMA_OUTPUT_INDEX (0u)
  #define ADC_DMA_BATTERY_INDEX (1u)
  #define ADC_DMA_USB_INDEX (2u)
  //开启时钟ADC的时钟
  rcu_osci_on(RCU_IRC28M);
  while(ERROR == rcu_osci_stab_wait(RCU_IRC28M));
  /* 配置ADC时钟 */
  rcu_adc_clock_config(RCU_ADCCK_IRC28M_DIV2);
  /* 使能ADC时钟 */
  rcu_periph_clock_enable(RCU_ADC);
  //=========================================================
  /* 端口时钟使能 */
  rcu_periph_clock_enable(ADC_GPIO_CLK);
  /* 配置读取ADC引脚 */
  gpio_mode_set(ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, ADC_GPIO_PIN);
  //=========================================================
  //连续转换关闭
  adc_special_function_config(ADC_CONTINUOUS_MODE, DISABLE);
  //打开扫描模式
  adc_special_function_config(ADC_SCAN_MODE, ENABLE);
  //=========================================================
  /* ADC 触发模式 用定时器1通道触发*/
  adc_external_trigger_source_config( ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_T1_CH1);
  /* 数据对齐方式 */
  adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
  /* ADC分辨率 12B */
  adc_resolution_config(ADC_RESOLUTION_12B);
  //=========================================================
  /* ADC channel 个数配置 */
  adc_channel_length_config( ADC_REGULAR_CHANNEL, _ADC_VALUE_NUMBER);
  /* 配置ADC通道 */
  adc_regular_channel_config(ADC_DMA_OUTPUT_INDEX , ADC_CHANNEL_4, ADC_SAMPLETIME_239POINT5); //输出功率
  adc_regular_channel_config(ADC_DMA_BATTERY_INDEX, ADC_CHANNEL_5, ADC_SAMPLETIME_239POINT5); //电池通道
  adc_regular_channel_config(ADC_DMA_USB_INDEX , ADC_CHANNEL_8, ADC_SAMPLETIME_239POINT5); //USB电压
  //使能adc规则通道外部触发器
  adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);
  /* 开启ADC DMA */
  adc_dma_mode_enable();
  /* ADC使能 */
  adc_enable();
  delay_1ms(2U);
  /* ADC校准复位 */
  adc_calibration_enable();
  3.配置定时器定时触发
  //将定时器配置成周期10ms
  timer_oc_parameter_struct timer_ocintpara;
  timer_parameter_struct timer_initpara;
  rcu_periph_clock_enable(RCU_TIMER1);
  /* TIMER1 configuration */
  //这里需要注意!!!!!因为我的主频是8M所以定时器这样配置,如果不一样,需要根据实际频率来调节
  timer_initpara.prescaler = 8-1;
  timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
  timer_initpara.counterdirection = TIMER_COUNTER_UP;
  timer_initpara.period = 10*1000;
  timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
  timer_initpara.repetitioncounter = 0U;
  timer_init(TIMER1,&timer_initpara);
  /* CH0 configuration in PWM mode1 */
  timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
  timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
  timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);
  //这里设置比较值是5*1000 5ms是触发点,但是由于是周期性,所以每次采集也是等间距的
  timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 5*1000);
  timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM0);
  timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);
  timer_auto_reload_shadow_enable(TIMER1);
  // 可以尝试让他输出PWM用示波器查看配置是否正确
  // gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
  // gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_0);
  // gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_0);
  4.中断函数和获取足够数据后进行数据处理
  //当传输完成足够的数据后,就关闭定时器,设置标志位,进行处理,可以在中断中处理,
  //也可以在其他地方处理,看个人习惯
  void DMA_Channel0_IRQHandler(void)
  {
  //传输完成标志位
  if(dma_interrupt_flag_get(DMA_CH0, DMA_INT_FLAG_FTF))
  {
  //设置标志位 DMA完成了
  flag_DMA_ADC_accomplish = TRUE;
  //关闭定时器,停止继续读取数据
  timer_disable(TIMER1);
  //清除全部标志位
  dma_interrupt_flag_clear(DMA_CH0, DMA_INT_FLAG_G);
  }
  }
  数据处理,这里我就做了个简单的求平均
  //dma平均之后的值
  uint32_t voltage_raw_value[3] = {0};
  //DMA传输完成 这里进行转换
  if(TRUE == flag_DMA_ADC_accomplish)
  {
  //清除之前的数据
  memset(voltage_raw_value,0,sizeof(voltage_raw_value));
  for(int i = 0;i 《 ADC_VALUE_ARRY_SIZE;i += 3)
  {
  voltage_raw_value[0] += battery_ADC_value_arry[i + 0];
  voltage_raw_value[1] += battery_ADC_value_arry[i + 1];
  voltage_raw_value[2] += battery_ADC_value_arry[i + 2];
  }
  }
  float tmp = 0;
  for(int i = 0;i 《 _ADC_VALUE_NUMBER;i += 1)
  {
  tmp = voltage_raw_value[i];
  //这里除50是求平均值
  tmp = tmp / 50;
  voltage_raw_value[i] = (uint16_t)tmp;
  }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分