手册中关于STM32WBA55CG开发板评模数转换器(ADC4)的说明如下:
12位ADC4是逐次逼近模数转换器。
它已经达到19个多路复用通道,使其能够测量来自9个外部和3个内部的信号源(其他频道保留)。
各种通道的ADC转换可以在单次、连续、扫描或不连续模式下执行。
ADC的结果为存储在左对齐或右对齐的16位数据寄存器中。模拟监视器功能允许应用程序检测输入电压是否下降超出用户定义的较高或较低阈值。
实现了一种高效的低功耗模式,以允许在低功耗下实现非常低的功耗频率。
ADC4在低功耗模式至停止模式下都是自主的。
内置的硬件过采样可以提高模拟性能,同时减轻CPU的相关计算负担。
本文将通过ADC4实现对芯片温度、参考电压、外部输入电压的采集并通过BLE上报手机。
前提:需要在一个已配置成功的BLE从机的程序基础上进行修改。
一、STM32CubeMX配置
1、ADC4配置
用到了3个通道IN2、Temperature Sensor Channel、Vrefint Channel



因为要进行3路通道采集,因此需要配置3个rank。
2、DMA配置


对应的代码:
static void MX_ADC4_Init(void)
{
/* USER CODE BEGIN ADC4_Init 0 */
/* USER CODE END ADC4_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC4_Init 1 */
/* USER CODE END ADC4_Init 1 */
/** Common config
*/
hadc4.Instance = ADC4;
hadc4.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV4;
hadc4.Init.Resolution = ADC_RESOLUTION_12B;
hadc4.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc4.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc4.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc4.Init.LowPowerAutoPowerOff = DISABLE;
hadc4.Init.LowPowerAutonomousDPD = ADC_LP_AUTONOMOUS_DPD_DISABLE;
hadc4.Init.LowPowerAutoWait = DISABLE;
hadc4.Init.ContinuousConvMode = DISABLE;
hadc4.Init.NbrOfConversion = 3;
hadc4.Init.DiscontinuousConvMode = DISABLE;
hadc4.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc4.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc4.Init.DMAContinuousRequests = ENABLE;
hadc4.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_LOW;
hadc4.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc4.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_79CYCLES_5;
hadc4.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_79CYCLES_5;
hadc4.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc4) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_VREFINT;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_TEMPSENSOR;
sConfig.Rank = ADC_REGULAR_RANK_3;
if (HAL_ADC_ConfigChannel(&hadc4, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC4_Init 2 */
/* USER CODE END ADC4_Init 2 */
}static void MX_GPDMA1_Init(void)
{
/* USER CODE BEGIN GPDMA1_Init 0 */
/* USER CODE END GPDMA1_Init 0 */
/* Peripheral clock enable */
__HAL_RCC_GPDMA1_CLK_ENABLE();
/* GPDMA1 interrupt Init */
HAL_NVIC_SetPriority(GPDMA1_Channel0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel0_IRQn);
HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);
HAL_NVIC_SetPriority(GPDMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(GPDMA1_Channel3_IRQn);
/* USER CODE BEGIN GPDMA1_Init 1 */
/* USER CODE END GPDMA1_Init 1 */
/* USER CODE BEGIN GPDMA1_Init 2 */
/* USER CODE END GPDMA1_Init 2 */
}3、STM32_WPAN配置
重点:需要将USE_TEMPERATURE_BASED_RADIO_CALIBRATION设置为NO。
如果这里是YES,自动生成的程序将接管ADC4,用户进行修改将十分困难,直接在程序中启动ADC采集会死机。
设成NO,用户可以自由地使用ADC4。

二、程序修改
0、main函数增加
ADC校准、启动DMA
/* USER CODE BEGIN 2 */
/* Perform ADC calibration */
if (HAL_ADCEx_Calibration_Start(&hadc4) != HAL_OK)
{
/* Calibration Error */
Error_Handler();
}
#if 1
if (HAL_ADC_Start_DMA(&hadc4,
(uint32_t *)uhADCxConvertedData,
ADC_CONVERTED_DATA_BUFFER_SIZE
) != HAL_OK)
{
/* Error: ADC conversion start could not be performed */
Error_Handler();
}
#endif
/* USER CODE END 2 */ADC中断回调函数,设置ADC采集成功标志
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
/* Update status variable of DMA transfer */
ubDmaTransferStatus = 1;
}1、增加一个TASK ID:TASK_ADC
位置:app_conf.h
typedef enum
{
CFG_TASK_HW_RNG,
CFG_TASK_LINK_LAYER,
CFG_TASK_HCI_ASYNCH_EVT_ID,
CFG_TASK_BLE_HOST,
CFG_TASK_AMM,
CFG_TASK_BPKA,
CFG_TASK_FLASH_MANAGER,
CFG_TASK_BLE_TIMER_BCKGND,
/* USER CODE BEGIN CFG_Task_Id_t */
TASK_BUTTON_1,
TASK_ADC,
/* USER CODE END CFG_Task_Id_t */
CFG_TASK_NBR /* Shall be LAST in the list */
} CFG_Task_Id_t;2、注册TASK
位置:uint32_t MX_APPE_Init(void *p_param)函数中
/* USER CODE BEGIN APPE_Init_2 */ UTIL_SEQ_RegTask( 1U << TASK_ADC, UTIL_SEQ_RFU, ADC_process); //ADC TASK /* USER CODE END APPE_Init_2 */
任务TASK_ADC对应执行ADC_process函数
3、ADC_process函数
void ADC_process(void)
{
printf("ADC_process\r\n");
#if 1
if (HAL_ADC_Start(&hadc4) != HAL_OK)
{
/* ADC conversion start error */
printf("HAL_ADC_Start error!\r\n");
}
BSP_LED_On(LED_GREEN);
HAL_Delay(500);
BSP_LED_Off(LED_GREEN);
HAL_Delay(500);
if(ubDmaTransferStatus == 1)
{
uhADCxConvertedData_VrefAnalog_mVolt = __LL_ADC_CALC_VREFANALOG_VOLTAGE(uhADCxConvertedData[1], LL_ADC_RESOLUTION_12B);
uhADCxConvertedData_VoltageGPIO_mVolt = __LL_ADC_CALC_DATA_TO_VOLTAGE(uhADCxConvertedData_VrefAnalog_mVolt, uhADCxConvertedData[0], LL_ADC_RESOLUTION_12B);
uhADCxConvertedData_VrefInt_mVolt = __LL_ADC_CALC_DATA_TO_VOLTAGE(uhADCxConvertedData_VrefAnalog_mVolt, uhADCxConvertedData[1], LL_ADC_RESOLUTION_12B);
hADCxConvertedData_Temperature_DegreeCelsius = __LL_ADC_CALC_TEMPERATURE(uhADCxConvertedData_VrefAnalog_mVolt, uhADCxConvertedData[2], LL_ADC_RESOLUTION_12B);
printf("uhADCxConvertedData_VrefAnalog_mVolt:%d\r\n",uhADCxConvertedData_VrefAnalog_mVolt);
printf("uhADCxConvertedData_VoltageGPIO_mVolt:%d\r\n",uhADCxConvertedData_VoltageGPIO_mVolt);
printf("uhADCxConvertedData_VrefInt_mVolt:%d\r\n",uhADCxConvertedData_VrefInt_mVolt);
printf("hADCxConvertedData_Temperature_DegreeCelsius:%d\r\n",hADCxConvertedData_Temperature_DegreeCelsius);
ubDmaTransferStatus = 0;
//Notification
formatSendData(uhADCxConvertedData_VrefAnalog_mVolt);//模拟参考电压
update_ADC_data_to_char(a_ADC_UpdateCharData);
formatSendData(uhADCxConvertedData_VoltageGPIO_mVolt);//GPIO PA7输入电压采集
update_ADC_data_to_char(a_ADC_UpdateCharData);
formatSendData(uhADCxConvertedData_VrefInt_mVolt);//内部参考电压1.2v
update_ADC_data_to_char(a_ADC_UpdateCharData);
formatSendData(hADCxConvertedData_Temperature_DegreeCelsius);//片内温度传感器
update_ADC_data_to_char(a_ADC_UpdateCharData);
//SERVICE1_Data_t service1_notification_data;
}
else UTIL_SEQ_SetTask(1U << TASK_ADC, CFG_SEQ_PRIO_0);//如果中断没返回,重新调用TASK_ADC 任务
#if 1
if(ubDmaTransferStatus==0)//ADC采集结束,再次调用HAL_ADC_Start_DMA,为下次采样做准备
{
HAL_Delay(500);
if (HAL_ADC_Start_DMA(&hadc4,
(uint32_t *)uhADCxConvertedData,
3
) != HAL_OK)
{
printf("HAL_ADC_Start_DMA error!\r\n");
}
}
#endif
#endif
}格式化采集数据函数:
void formatSendData(uint32_t x)
{
a_ADC_UpdateCharData[0] = (x >> 24) & 0xFF; // 最高有效字节
a_ADC_UpdateCharData[1] = (x >> 16) & 0xFF;
a_ADC_UpdateCharData[2] = (x >> 8) & 0xFF;
a_ADC_UpdateCharData[3] = x & 0xFF; // 最低有效字节
}4、update_ADC_data_to_char函数
位于serivce1_app.c(service1是BLE的服务名)
/* USER CODE BEGIN FD_LOCAL_FUNCTIONS*/
void update_ADC_data_to_char(uint8_t *updateData)
{
tBleStatus result = BLE_STATUS_INVALID_PARAMS;
SERVICE1_Data_t msg_conf;
msg_conf.p_Payload=updateData;
msg_conf.Length=sizeof(updateData);
result = SERVICE1_UpdateValue(SERVICE1_ADCCHAR, &msg_conf);
if( result != BLE_STATUS_SUCCESS )
{
LOG_INFO_APP("Sending of Report Map Failed error 0x%X\n", result);
}
}
/* USER CODE END FD_LOCAL_FUNCTIONS*/通过调用SERVICE1_UpdateValue函数将采集值notification出去。
5、增加一个按键触发
按下B2,触发TASK_ADC任务,执行ADC_process。
/* USER CODE BEGIN 4 */
void BSP_PB_Callback(Button_TypeDef Button)
{
switch(Button)
{
case B1:
UTIL_SEQ_SetTask(1U << TASK_BUTTON_1, CFG_SEQ_PRIO_0);
break;
case B2:
UTIL_SEQ_SetTask(1U << TASK_ADC, CFG_SEQ_PRIO_0);
break;
case B3:
printf("press B3\r\n");
break;
default:
break;
}
}三、运行效果
1、log输出

2、蓝牙调试助手


我要赚赏金
