STM32C031拥有12 位 ADC 是逐次趋近型模数转换器。它具有多达 23 个复用通道,可测量来自 19 个外部源和 4 个内部源的信号。各种不同通道的 A/D 转换可在单次、连续、扫描或不连续采样模式下进行。 ADC 的结果存储在一个左对齐或右对齐的 16 位数据寄存器中。ADC 具有模拟看门狗特性,允许应用检测输入电压是否超过了用户自定义的阈值上限或下限。采用了高效的低功耗模式,在低频下可实现极低的功耗。内置硬件过采样器,可提高模拟性能,同时还能减轻 CPU 进行相关计算的负担。
下面是ADC外设的特性:
下面就来测试一下ADC采样吧。首先打卡STM32CubeMX配置ADC外设功能。
选了外部2路采样端口PA0和PA1,以及内部电压通道和温度传感器通道。
接下来配置ADC初始化参数。
使能ADC采样中断。
好了,接下来生成代码。然后添加代码读取ADC采样值。
下面是ADC的生成初始化代码了。
/**
* @brief ADC1 Initialization Function
* @param None
* @retval None
*/
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.LowPowerAutoPowerOff = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 4;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_79CYCLES_5;
hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_12CYCLES_5;
hadc1.Init.OversamplingMode = DISABLE;
hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
接下来重写ADC采样完成的中断回调函数。其中ADC_FLAG_EOC表示每个通道转换完成中断标志。ADC_FLAG_EOS表示一个序列组内所有通道采样转换完成标志。
//adc回调
uint32_t adc_value[4];
volatile uint32_t adc_index = 0;
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if( __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOC))
{
adc_value[adc_index++] = HAL_ADC_GetValue(hadc);
}
if( __HAL_ADC_GET_FLAG(hadc, ADC_FLAG_EOS))
{
adc_index = 0;
printf("%u\t %u\t",adc_value[0],adc_value[1]);
printf("%u\t %u\r\n",adc_value[2],adc_value[3]);
}
}
下面我使用了串口SHELL方式命令控制转换和定时启动转换。
void shell_adc(char argc, char *argv)
{
uint16_t ps;
uint32_t als;
printf("Start ADC...\r\n");
HAL_ADC_Start_IT(&hadc1); //中断方式启动ADC1转换。
}
NR_SHELL_CMD_EXPORT(adc, shell_adc, "Start ADC.");
//主循环中调用触发采样。并在OLED上显示
if(timeout_1 == 0)
{
timeout_1 = 100; //100MS
HAL_ADC_Start_IT(&hadc1); //中断方式启动ADC1转换。
sprintf(buff,"A0:%d",adc_value[0]);
ssd1306_SetCursor(0,0);
ssd1306_WriteString(buff, Font_11x18, White);
sprintf(buff,"A1:%d",adc_value[1]);
ssd1306_SetCursor(0,24);
ssd1306_WriteString(buff, Font_11x18, White);
ssd1306_UpdateScreen();
}
然后编译下载程序测试。
效果如下,看到ADC采样的数据很稳定,跳动的幅度很小。
大佬