本帖最后由 dirty 于 2024-1-25 14:16 编辑
本篇讲述使用定时器与ADC,达到定时1s间隔读取ADC值。
一.关于外设了解
开发板上有一颗可调电阻R57,接在MCU的PC11引脚,复用为ADC_IN6,使用12bit ADC功能。MCU有一个16位基本定时器(BSTIM16),作为定时用。
图1:ADC原理图接线
二.代码部分
1. 在mf_config.c添加ADC 与 定时器初始化函数,并将这些初始化放在MF_Config_Init以供调用。
(1).关于ADC初始化。有配置ADC引脚、通道选择、ADC位宽等
/**
* [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*/
}
在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)关于基础定时器初始化。包含有定时器预分频、自动重装载值以及定时中断初始化
/**
* @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.在adc.c添加,使用ADC DMA 采集数据,在DMA_IRQHandler中断里置位ADC完成标志。
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中断置ADC完成标志位
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;
}
}
如下是计算ADC值
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.在bstim16_timing.c添加启动定时器函数、定时器中断服务函数 并在中断置位定时器标志
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.在app_config.h配资头文件使能相关宏
#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.在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
}
}
三.编译烧录及测试
全编译后,上电烧录,旋转可调电阻,查看日志。
图2:定时采集ADC 日志
从日志可以看出定时器1s间隔,且调节电阻器,ADC值 发生变化。达到功能预期。