This post was last edited by dirty on 2024-1-25 14:16
This article describes how to use a timer and ADC to read ADC values at regular 1s intervals.
1. Understanding of peripherals
There is an adjustable resistor R57 on the development board, which is connected to the PC11 pin of the MCU and multiplexed as ADC_IN6, using the 12-bit ADC function. The MCU has a 16-bit basic timer (BSTIM16) for timing.
Figure 1: ADC schematic wiring
2. Code part
1. Add ADC and timer initialization functions in mf_config.c and put these initializations in MF_Config_Init for calling.
(1) About ADC initialization. There are configurations of ADC pins, channel selection, ADC bit width, etc.
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] ADC_Common Initialization function
* @param void
* @retval None
*/
void MF_ADC_Common_Init(void)
{
/*ADC CONFIG*/
FL_ADC_CommonInitTypeDef CommonInitStruct;
CommonInitStruct.clockSource = FL_CMU_ADC_CLK_SOURCE_RCHF; /*配置ADC工作时钟源*/
CommonInitStruct.clockPrescaler = FL_ADC_CLK_PSC_DIV1; /*配置ADC工作时钟分频*/
CommonInitStruct.referenceSource = FL_ADC_REF_SOURCE_VDDA; /*配置ADC参考源*/
CommonInitStruct.bitWidth = FL_ADC_BIT_WIDTH_12B; /*配置ADC输出位宽*/
(void)FL_ADC_CommonInit(&CommonInitStruct);
}
/**
* @brief ADC Initialization function
* @param void
* @retval None
*/
void MF_ADC_Init(void)
{
FL_GPIO_InitTypeDef GPIO_InitStruct;
FL_ADC_InitTypeDef defaultInitStruct;
GPIO_InitStruct.pin = FL_GPIO_PIN_11; /*配置GPIO的引脚号*/
GPIO_InitStruct.mode = FL_GPIO_MODE_ANALOG; /*配置GPIO的功能模式*/
GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL; /*配置GPIO的输出模式*/
GPIO_InitStruct.pull = FL_GPIO_BOTH_DISABLE; /*配置GPIO上拉下拉模式*/
GPIO_InitStruct.remapPin = FL_GPIO_PINREMAP_FUNCTON1; /*配置GPIO数字重定向功能*/
GPIO_InitStruct.driveStrength = FL_GPIO_DRIVESTRENGTH_X3; /*配置GPIO驱动能力*/
GPIO_InitStruct.analogSwitch = FL_DISABLE; /*配置GPIO模拟开关功能*/
(void)FL_GPIO_Init( GPIOC, &GPIO_InitStruct ); /*GPIO初始化*/
defaultInitStruct.conversionMode = FL_ADC_CONV_MODE_SINGLE; /*配置ADC转换模式*/
defaultInitStruct.autoMode = FL_ADC_SINGLE_CONV_MODE_AUTO; /*配置ADC转换流程,仅对单次转换有效*/
defaultInitStruct.waitMode = FL_DISABLE; /*配置ADC等待模式:使用DMA搬运时必须禁止等待模式*/
defaultInitStruct.overrunMode = FL_ENABLE; /*配置ADC_Overrun模式*/
defaultInitStruct.scanDirection = FL_ADC_SEQ_SCAN_DIR_FORWARD; /*配置ADC扫描顺序*/
defaultInitStruct.externalTrigConv = FL_ADC_TRIGGER_EDGE_NONE; /*配置非软件触发使能及极性*/
defaultInitStruct.triggerSource = FL_ADC_TRGI_LUT0; /*配置ADC非软件触发源*/
defaultInitStruct.fastChannelTime = FL_ADC_FAST_CH_SAMPLING_TIME_4_ADCCLK; /*配置ADC快速通道采样时间*/
defaultInitStruct.lowChannelTime = FL_ADC_SLOW_CH_SAMPLING_TIME_192_ADCCLK; /*配置ADC慢速通道采样时间*/
defaultInitStruct.oversamplingMode = FL_DISABLE; /*配置ADC过采样模式*/
defaultInitStruct.overSampingMultiplier = FL_ADC_OVERSAMPLING_MUL_16X; /*配置ADC过采样率*/
defaultInitStruct.oversamplingShift = FL_ADC_OVERSAMPLING_SHIFT_4B; /*配置ADC过采样结果移位*/
(void)FL_ADC_Init(ADC,&defaultInitStruct ); /*ADC初始化*/
FL_ADC_EnableSequencerChannel(ADC, FL_ADC_INTERNAL_AVREF); /*通道选择*/
FL_ADC_EnableSequencerChannel(ADC, FL_ADC_EXTERNAL_CH6);
FL_ADC_EnableDMAReq(ADC); /*配置ADC_DMA*/
}
Initialize peripherals in MF_Config_Init
/**
* @brief The application entry point.
* @retval int
*/
void MF_Config_Init(void)
{
/*FUNCTION CALL*/
//UART Printf
MF_UART4_Init();
//ADC Init
#if(FUNCTION_ADC==1)
MF_ADC_Common_Init(); /*ADC基础配置*/
MF_ADC_Init(); /*ADC初始化配置*/
printf("adc init\r\n");
#endif
//Base Timer Init
MF_BSTIM16_TimerBase_Init();
MF_BSTIM16_Interrupt_Init();
MF_NVIC_Init();
printf("basetimer init\r\n");
}
(2) About basic timer initialization. Including timer pre-scaling, automatic reload value and timer interrupt initialization
/**
* @brief BSTIM16_TimerBase Initialization function
* @param void
* @retval None
*/
void MF_BSTIM16_TimerBase_Init(void)
{
FL_BSTIM16_InitTypeDef TimerBase_InitStruct;
TimerBase_InitStruct.prescaler = 7999;
TimerBase_InitStruct.autoReload = 999;
TimerBase_InitStruct.autoReloadState = FL_ENABLE;
TimerBase_InitStruct.clockSource = FL_CMU_BSTIM16_CLK_SOURCE_APBCLK;
(void)FL_BSTIM16_Init(BSTIM16, &TimerBase_InitStruct);
}
/**
* @brief BSTIM16 Interrupt Initialization function
* @param void
* @retval None
*/
void MF_BSTIM16_Interrupt_Init(void)
{
FL_BSTIM16_ClearFlag_Update(BSTIM16);
FL_BSTIM16_EnableIT_Update(BSTIM16);
}
/**
* @brief NVIC Initialization function
* @param void
* @retval None
*/
void MF_NVIC_Init(void)
{
FL_NVIC_ConfigTypeDef InterruptConfigStruct;
InterruptConfigStruct.preemptPriority = 0x02;
FL_NVIC_Init(&InterruptConfigStruct, BSTIM_IRQn);
}
2. Add in adc.c, use ADC DMA to collect data, and set the ADC completion flag in the DMA_IRQHandler interrupt.
static void ADC_DMA_Common_Init(void)
{
FL_DMA_InitTypeDef DMA_InitStruct = {0};
FL_NVIC_ConfigTypeDef InterruptConfigStruct;
DMA_InitStruct.periphAddress = FL_DMA_PERIPHERAL_FUNCTION1; /* 配置DMA通道功能 */
DMA_InitStruct.direction = FL_DMA_DIR_PERIPHERAL_TO_RAM; /* 配置DMA通道方向 */
DMA_InitStruct.memoryAddressIncMode = FL_DMA_MEMORY_INC_MODE_INCREASE; /* 配置RAM的增减方向 */
DMA_InitStruct.dataSize = FL_DMA_BANDWIDTH_16B; /* 配置DMA传输位宽 */
DMA_InitStruct.priority = FL_DMA_PRIORITY_VERYHIGH; /* 配置DMA通道优先级:如应用于ADC通道,必须配置为最高 */
DMA_InitStruct.circMode = FL_DISABLE; /* 配置DMA通道循环缓存 */
(void)FL_DMA_Init(DMA, &DMA_InitStruct, FL_DMA_CHANNEL_0);
FL_DMA_Enable(DMA); /* 配置DMA全局开关 */
FL_DMA_ClearFlag_TransferComplete(DMA,FL_DMA_CHANNEL_0); /* 清标志 */
FL_DMA_EnableIT_TransferComplete(DMA,FL_DMA_CHANNEL_0); /* 配置DMA全程中断 */
InterruptConfigStruct.preemptPriority = 2U; /* 配置DMA的优先级 */
FL_NVIC_Init(&InterruptConfigStruct,DMA_IRQn );
}
void ADC_DMA_Config(uint16_t *buffer, uint32_t length)
{
FL_DMA_ConfigTypeDef DMA_ConfigStruct = {0};
DMA_ConfigStruct.memoryAddress = (uint32_t)buffer; /* 配置DMA_RAM地址 */
DMA_ConfigStruct.transmissionCount = length - 1; /* 配置DMA传输长度 */
(void)FL_DMA_StartTransmission(DMA, &DMA_ConfigStruct, FL_DMA_CHANNEL_0);
}
uint16_t DMAResult[ADC_VALUE_NUM];//2
void ADC_Config(void)
{
FL_PMU_EnableAVREFBuffer(PMU); /* 使能VREF BUFFER */
ADC_DMA_Common_Init(); /* DMA基础初始化配置 */
ADC_DMA_Config(DMAResult, ADC_VALUE_NUM);//2 /* DMA初始化配置 */
FL_ADC_ClearFlag_EndOfConversion(ADC); /* 清标志 */
FL_ADC_Enable(ADC); /* 启动ADC */
FL_ADC_EnableSWConversion(ADC); /* 开始转换 */
}
DMA interrupt sets ADC completion flag
volatile uint8_t ADCComplete = 0;
void DMA_IRQHandler(void)
{
uint32_t IE_Flag,IF_Flag;
IE_Flag = FL_DMA_IsEnabledIT_TransferComplete(DMA,FL_DMA_CHANNEL_0); /* 获取中断使能以及中断标志状态 */
IF_Flag = FL_DMA_IsActiveFlag_TransferComplete(DMA,FL_DMA_CHANNEL_0);
if((0x01U == IE_Flag) && (0x01U == IF_Flag))
{
FL_DMA_ClearFlag_TransferComplete(DMA,FL_DMA_CHANNEL_0); /* 清标志 */
FL_ADC_ClearFlag_EndOfConversion(ADC);
ADCComplete = 1;
}
}
The ADC value is calculated as follows
uint32_t GetSingleChannelVoltage_DMA(void)
{
uint32_t GetChannelVoltage = 0;
if (DMAResult[0] != 0)
{
GetChannelVoltage = (uint32_t)(((uint64_t)DMAResult[0] * ADC_VREF) / ((uint64_t)DMAResult[1] * 10));
}
return GetChannelVoltage;
}
3. Add the start timer function and timer interrupt service function in bstim16_timing.c and set the timer flag in the interrupt
volatile uint8_t time_flag = 0;
/**
* @brief 使能BSTIM16
* @param void
* @retval None
*/
void BSTIM16_Start(void)
{
/* 使能BSTIM16 */
FL_BSTIM16_Enable(BSTIM16);
}
/**
* @brief BSTIM中断服务函数
* @param void
* @retval None
*/
void BSTIM_IRQHandler(void)
{
uint32_t BSTIMUpdateIT = 0;
uint32_t BSTIMUpdateFlag = 0;
BSTIMUpdateIT = FL_BSTIM16_IsEnabledIT_Update(BSTIM16);
BSTIMUpdateFlag = FL_BSTIM16_IsActiveFlag_Update(BSTIM16);
if ((BSTIMUpdateIT == 0x01U) && (BSTIMUpdateFlag == 0x01U))
{
FL_BSTIM16_ClearFlag_Update(BSTIM16);
time_flag=1;
printf("process timer things...\r\n");
/*
处理更新事件...
*/
}
}
4. Enable related macros in the app_config.h header file
#ifndef _APP_CONFIG_H_
#define _APP_CONFIG_H_
#ifdef __cplusplus
extern "C"{
#endif
#include "main.h"
#include "fm33ft0xxa_fl.h"
#include "iwdt.h"
#include "svd.h"
#include "rmu.h"
#include "led.h"
#include "adc.h"
#include "atim.h"
#include "bstim16_timing.h"
#define FUNCTION_LED 0
#define FUNCTION_ADC 1
#define ADC_VALUE_NUM 2
#ifdef __cplusplus
}
#endif
#endif
5. Add initialization and function functions in main.c
int main(void)
{
/* 使能IWDT */
IWDT_Init(FL_IWDT_PERIOD_4000MS);
/* Initialize FL Driver Library */
/* SHOULD BE KEPT!!! */
FL_Init();
/* 使能SVD, 阈值4.157V(falling)~4.257V(rising) */
SVD_Init(SVD_MONTIOR_VDD, FL_SVD_WARNING_THRESHOLD_GROUP11, FL_SVD_REFERENCE_1P0V);
/* 确认SVD监测结果是否高于阈值,如否则持续等待 */
while(false == SVD_Result_Confirmed(SVD_HIGHER_THRESHOLD, 2000U/*us*/));
/* 使能BOR */
RMU_BOR_Init(FL_RMU_BOR_THRESHOLD_2P00V);
MF_Config_Init();
#if(FUNCTION_ADC==1)
ADC_Config();
#endif
BSTIM16_Start();
printf("All Peripher init finish\r\n");
while(1)
{
/* 清狗 */
FL_IWDT_ReloadCounter(IWDT);
/* 电源掉电监测处理 */
PowerDownMonitoring();
/* 功能执行 */
#if(FUNCTION_LED==1)
LED_TOG_FUN(RED, 1, FL_DelayMs(200));
LED_TOG_FUN(GREEN, 1, FL_DelayMs(200));
LED_TOG_FUN(BULE, 1, FL_DelayMs(200));
#endif
#if(FUNCTION_ADC==1)
if((0x01U == ADCComplete)&&(time_flag==1))
{
ADCComplete = 0;
time_flag=0;
GetVoltage = GetSingleChannelVoltage_DMA();
printf("Res_ADC:%d\r\n",GetVoltage);
ADC_DMA_Config(DMAResult, ADC_VALUE_NUM);
FL_ADC_ClearFlag_EndOfConversion(ADC);
FL_ADC_Enable(ADC);
FL_ADC_EnableSWConversion(ADC);
}
#endif
}
}
3. Compile, burn and test
After full compilation, power on and burn, rotate the adjustable resistor, and check the log.
Figure 2: Timed acquisition of ADC logs
From the log, we can see that the timer has a 1s interval and the ADC value changes after adjusting the resistor. This meets the functional expectations.