stm32多通道ADC采集

先说下注意或易错的地方

  1. 需要使用一个ADC的一个通道,采用扫描模式
  2. 如果还使用FreeRTOS还要注意优先级问题,初始化需要在开启采集之前
  3. 单通道ADC采集时不需要开启内存自增加,DMA_InitSturcture.DMA_MemoryInc = DMA_MemoryInc_Disable;。多通道ADC采集时需要开启内存自增加模式DMA_InitSturcture.DMA_MemoryInc = DMA_MemoryInc_Enable;
  4. 如果使用数组保存采集的数据,需要注意数据尺寸。DMA_PeripheralDataSize, DMA_MemoryDataSize它们会决定你采集的数据保存在数组的那个位置
  # 如果保存的数组为32位,数据又设置的半字,数据就会将32位数组分为高16和低16,因此在取用数据时就要左移或右移操作
  __IO uint32_t ADC_DualConvertedValueTab[4];
  DMA_InitSturcture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitSturcture.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  # 如上DMA会将第一个采集到的数据放到ADC_DualConvertedValueTab[0]的低16位,下一个数据放到ADC_DualConvertedValueTab[0]的高16位,因此`DMA_InitSturcture.DMA_BufferSize`就可以设置为开启通道数的一半,这样扫描一圈,缓存就刚好使用完

  # 如果像下面这样设置
  __IO uint32_t ADC_DualConvertedValueTab[4];
  DMA_InitSturcture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitSturcture.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  # 数组中的每个数据都对应一个采集结果,对于后面操作更方便

  # 如果非想用半字将保存数据数组声明为16位的也是可以的
  __IO uint16_t ADC_DualConvertedValueTab[4];
  DMA_InitSturcture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitSturcture.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  1. 已经设置采集信号的通道一定要接入一个确定的电平,不然采集到的值会是一个不可控的值,不一定会是0
  2. 试例代码中有个开启测试监测的配置,我试了下不开好像也没什么问题

Show me the Code

  1. 我有用一个DAC生成一个可变的电压信号,下面是初始化代码,用的是GPIOA4
#include "dac.h"

// 初始化后在软件中设置数字信号值
void dac_init(void){
  DAC_InitTypeDef DAC_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  DAC_InitStructure.DAC_Trigger = DAC_Trigger_Software;
  // 可以用于生成三角波等,这里不使用这个功能
  DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_Noise;
  DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits8_0;
  // 开启输出缓存可以增加驱动能力
  DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;

  // STM32的DAC通道只有2个,且是固定的PA4,PA5
  DAC_Init(DAC_Channel_1, &DAC_InitStructure);

  // 开启
  DAC_Cmd(DAC_Channel_1, ENABLE);

  DAC_SetChannel1Data(DAC_Align_12b_L, 0x7FF0);
  DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
}
  1. 接下来是串口的代码,这个没什么说的,一般也都会,我有开接收中断,所以会长一点
#include "serial.h"

QueueHandle_t uart1_rx_receive;
extern QueueHandle_t command_buf;

// 调试信息输出
void debug_printf(char *str){
  while(*str++){
    USART_SendData(USART1, *str);
		// 等待数据发送完成
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) ;
  }
}

// 中断优先级设置
static void NVIC_Config(void);

// 串口初始化
void serial_init(void){
  GPIO_InitTypeDef gpio_init;

	// 引脚设置
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

  gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
  gpio_init.GPIO_Pin = GPIO_Pin_10;

  GPIO_Init(GPIOA, &gpio_init);

  gpio_init.GPIO_Mode = GPIO_Mode_AF_PP;
  gpio_init.GPIO_Pin = GPIO_Pin_9;
  GPIO_Init(GPIOA, &gpio_init);

  // 串口设置
  USART_InitTypeDef usartInit;

  usartInit.USART_BaudRate = 115200;
  usartInit.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  usartInit.USART_WordLength = USART_WordLength_8b;
  usartInit.USART_StopBits = USART_StopBits_1;
  usartInit.USART_Parity = USART_Parity_No;
  usartInit.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

  USART_Init(USART1, &usartInit);

	// 开启接收中断
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

  NVIC_Config();

  // 开启串口
  USART_Cmd(USART1, ENABLE);
}

// 接收中断优先级设置
void NVIC_Config(void){
  NVIC_InitTypeDef nvicInit;

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

  nvicInit.NVIC_IRQChannel = USART1_IRQn;
  nvicInit.NVIC_IRQChannelSubPriority = 1;
  nvicInit.NVIC_IRQChannelPreemptionPriority = 0;
  nvicInit.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&nvicInit);
}

// 硬件中断函数
void USART1_IRQHandler(void){
  uint8_t ch;
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
      ch = USART_ReceiveData(USART1);
      xQueueSendFromISR(uart1_rx_receive, &ch, NULL);
    }
    USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}

// 用于printf()
int fputc(int c, FILE *file){
  USART_SendData(USART1, c);
  while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) ;
  return c;
}
  1. 下面就是ADC多通道采集的代码
#include "muiltichannel_adc_use_dma.h"

__IO uint16_t ADC_DualConvertedValueTab[4];

static void muiltichannel_rcc_init(void){

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
  RCC_ADCCLKConfig(RCC_PCLK2_Div4);

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

}

static void muiltichannel_gpio_init(void){
  GPIO_InitTypeDef GPIO_InitStructure;

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

  GPIO_Init(GPIOA, &GPIO_InitStructure);
}

static void muiltichannel_dma_init(void){

  DMA_InitTypeDef DMA_InitSturcture;

  DMA_DeInit(DMA1_Channel1);
  DMA_InitSturcture.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;
  // 这里因为前面是用数组保存接收数据,ADC_DualConvertedValueTab前面有没有&都可以
  DMA_InitSturcture.DMA_MemoryBaseAddr = (uint32_t)ADC_DualConvertedValueTab;
  DMA_InitSturcture.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitSturcture.DMA_BufferSize = 4;
  DMA_InitSturcture.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  // 这里一定使能
  DMA_InitSturcture.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitSturcture.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitSturcture.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitSturcture.DMA_Mode = DMA_Mode_Circular;
  DMA_InitSturcture.DMA_Priority = DMA_Priority_High;
  DMA_InitSturcture.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitSturcture);

  DMA_Cmd(DMA1_Channel1, ENABLE);
}

void muiltichannel_adc_init(void){
  ADC_InitTypeDef ADC_InitStructure;

  muiltichannel_rcc_init();
  muiltichannel_gpio_init();
	muiltichannel_dma_init();

  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 4;
  ADC_Init(ADC1, &ADC_InitStructure);

  // 这里就是温度开关
  ADC_TempSensorVrefintCmd(ENABLE);

  // 设置通道,此处通道要同GPIO设置时一一对应
  ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_55Cycles5);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_55Cycles5);
  ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 4, ADC_SampleTime_55Cycles5);

  // 准备开启ADC
  ADC_DMACmd(ADC1, ENABLE);
  ADC_Cmd(ADC1, ENABLE);

  ADC_ResetCalibration(ADC1);
  while(ADC_GetCalibrationStatus(ADC1));

  ADC_StartCalibration(ADC1);
  while(ADC_GetCalibrationStatus(ADC1));
}

// 多通道adc使能
void muiltichannel_adc_enable(void){
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
  1. main函数,这里我有使用FreeRTOS
#include "stm32f10x_conf.h"

extern __IO uint16_t ADC_DualConvertedValueTab[4];
extern QueueHandle_t command_buf;

// PA4引脚电平从大到小变化
void dac_task(void *pvParameter){
  static unsigned short num = 0xffff;
  for(;;){
    DAC_SetChannel1Data(DAC_Align_12b_L, num);
		DAC_SoftwareTriggerCmd(DAC_Channel_1, ENABLE);
    num -= 0xf;
    if(num < 0xfff)
      num = 0xffff;
		vTaskDelay(2);
  }
}

void system_init(void *pvParameter){
  SystemInit();
  dac_init();
  serial_init();
  muiltichannel_adc_init();
  command_buf = xQueueCreate(100, sizeof(struct command_t));
  vTaskDelete(NULL);
}

void print_test_task(void *pvParameter){
  for(;;){
    printf("This just test for uart\r\n");
    vTaskDelay(500);
  }
}

void muiltichannel_adc_task(void *pvParameter){
  muiltichannel_adc_enable();
  for(;;){
    printf("PA1 voltage is %x\r\n", ADC_DualConvertedValueTab[0]);
    printf("PA2 voltage is %x\r\n", ADC_DualConvertedValueTab[1]);
    printf("PA3 voltage is %x\r\n", ADC_DualConvertedValueTab[2]);
    printf("PA5 voltage is %x\r\n", ADC_DualConvertedValueTab[3]);
    vTaskDelay(50);
  }
}


// 这里说下测试方法,PA4连接PA1,PA2,PA3, PA5中的任意一个,其它的连接3.3V或GND
// 下载程序到板子,按复位就可以看到各引脚接收到的值,也可以将这个值 * 3.3 / 4096就是当前电压
// 连接PA4那个引脚的电压会一直变化
int main(void)
{
  // 这个是初始化任务,要先运行,所以优先级是2
  xTaskCreate(system_init, "system init task", 500, NULL, 2, NULL);
  //xTaskCreate(print_test_task, "uart print test", 200, NULL, 1, NULL);
  xTaskCreate(dac_task, "dac task for led breath", 1000, NULL, 1, NULL);
  xTaskCreate(muiltichannel_adc_task, "mNuiltichannel adc test task", 1000, NULL, 1, NULL);

	vTaskStartScheduler();
}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐