写在前面:感谢XXX大佬的指导,点击可查看他的博客
用定时器TIM产生PWM波来控制ADC的采样频率,在ADC中断中将采样值直接通过DAC输出。本文主要展示ADC、TIM、DAC的配置(hal库)
主要的困难是通过定时器TIM触发ADC采样的配置比较复杂,定时器的配置还没太懂
ADC_HandleTypeDef ADC1_Handler;//ADC句柄
ADC_ChannelConfTypeDef ADC1_ChanConf;
//初始化ADC
//ch: ADC_channels
//通道值 0~16取值范围为:ADC_CHANNEL_0~ADC_CHANNEL_16
void MY_ADC_Init(void)
{
ADC1_Handler.Instance=ADC1;
ADC1_Handler.Init.ClockPrescaler=ADC_CLOCK_SYNC_PCLK_DIV4; //4分频,ADCCLK=PCLK2/4=90/4=22.5MHZ ,不使用软件触发而使用外部触发时,ADC时钟不影响采样率
ADC1_Handler.Init.Resolution=ADC_RESOLUTION_12B; //12位模式
ADC1_Handler.Init.DataAlign=ADC_DATAALIGN_RIGHT; //右对齐
ADC1_Handler.Init.ScanConvMode=DISABLE; //非扫描模式;
ADC1_Handler.Init.EOCSelection=ADC_EOC_SINGLE_CONV; //DISABLE--关闭EOC中断
ADC1_Handler.Init.ContinuousConvMode=DISABLE; //关闭连续转换
ADC1_Handler.Init.NbrOfConversion=1; //1个转换在规则序列中 也就是只转换规则序列1
ADC1_Handler.Init.DiscontinuousConvMode=DISABLE; //禁止不连续采样模式
ADC1_Handler.Init.NbrOfDiscConversion=0; //不连续采样通道数为0
ADC1_Handler.Init.ExternalTrigConv=ADC_EXTERNALTRIGCONV_T1_CC1;//外部触发方式;使用定时器TIM产生PWM来触发ADC采样,PWM波的频率就是这种模式下的采样频率
ADC1_Handler.Init.ExternalTrigConvEdge=ADC_EXTERNALTRIGCONVEDGE_RISING;//外部边沿触发
// ADC1_Handler.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;//使用软件触发
// ADC1_Handler.Init.ExternalTrigConv = ADC_SOFTWARE_START;//软件触发
ADC1_Handler.Init.DMAContinuousRequests=DISABLE; //关闭DMA请求
HAL_ADC_Init(&ADC1_Handler); //初始化
ADC1_ChanConf.Channel=ADC_CHANNEL_5; //通道5
ADC1_ChanConf.Rank=1; //第1个序列,序列1
ADC1_ChanConf.SamplingTime=ADC_SAMPLETIME_3CYCLES; //采样时间;这个越小,单次采样(ADC转换)时间越短,但精度可能受影响
ADC1_ChanConf.Offset=0;
HAL_ADC_ConfigChannel(&ADC1_Handler,&ADC1_ChanConf); //通道配置
HAL_NVIC_SetPriority(ADC_IRQn,0,0); //配置ADC中断的优先级
HAL_NVIC_EnableIRQ(ADC_IRQn); //开启ADC中断
HAL_ADC_Start_IT(&ADC1_Handler); //允许中断并启动常规通道的ADC转换
}
//ADC底层驱动,引脚配置,时钟使能
//此函数会被HAL_ADC_Init()调用
//hadc:ADC句柄
void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_ADC1_CLK_ENABLE(); //使能ADC1时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_5; //PA5
GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}
//ADC中断服务函数
void ADC_IRQHandler(void)
{
HAL_ADC_IRQHandler(&ADC1_Handler);
}
// ADC常规转换完全回调函数,就是ADC采一个值就进入这个函数一次
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
//将ADC采的值直接通过DAC输出
HAL_DAC_SetValue(&DAC1_Handler,DAC_CHANNEL_1,DAC_ALIGN_12B_R,HAL_ADC_GetValue(&ADC1_Handler));//12位右对齐数据格式设置DAC值
}
TIM_HandleTypeDef TIM1_Handler; //定时器句柄
TIM_OC_InitTypeDef TIM1_CH1Handler; //定时器1通道1句柄
//这个暂时不知道
TIM_MasterConfigTypeDef sMasterConfig;
//TIM14 PWM部分初始化
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
void TIM1_PWM_Init(u16 arr,u16 psc)
{
TIM1_Handler.Instance=TIM1; //定时器1
TIM1_Handler.Init.Prescaler=psc; //定时器分频
TIM1_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;//向上计数模式
TIM1_Handler.Init.Period=arr; //自动重装载值
TIM1_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&TIM1_Handler); //初始化PWM
//这个很重要,还没懂
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;// 主模式选择。选择具体模式发送到TRG0上。
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;//主定时器的从模式使能与失能位
TIM1_CH1Handler.OCMode=TIM_OCMODE_PWM1; //模式选择PWM1
TIM1_CH1Handler.Pulse=arr/2; //设置比较值,此值用来确定占空比,默认比较值为自动重装载值的一半,即占空比为50%
TIM1_CH1Handler.OCPolarity=TIM_OCPOLARITY_LOW; //输出比较极性为低
HAL_TIM_PWM_ConfigChannel(&TIM1_Handler,&TIM1_CH1Handler,TIM_CHANNEL_1);//配置TIM1通道1
HAL_TIM_PWM_Start(&TIM1_Handler,TIM_CHANNEL_1);//开启PWM通道1
}
//定时器底层驱动,时钟使能,引脚配置
//此函数会被HAL_TIM_PWM_Init()调用
//htim:定时器句柄
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_TIM1_CLK_ENABLE(); //使能定时器1
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_8; //PA8
//配置完成后可在该引脚观察到PWM波,波的频率就是ADC的采样频率
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate= GPIO_AF1_TIM1; //PA8复用为TIM1_CH1
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}
系统时钟设置(正点原子STM32F4例程的sys.c为例)
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //时钟源为HSE
选择时钟源为HSE;HSE 是高速外部时钟,开发板接的是 8M 的晶振
RCC_OscInitStructure.PLL.PLLM=pllm; //主PLL和音频PLL分频系数(PLL之前的分频),取值范围:2~63.
RCC_OscInitStructure.PLL.PLLN=plln; //主PLL倍频系数(PLL倍频),取值范围:64~432.
RCC_OscInitStructure.PLL.PLLP=pllp; //系统时钟的主PLL分频系数(PLL之后的分频),取值范围:2,4,6,8.(仅限这4个值!)
//下面这个暂时用不上
RCC_OscInitStructure.PLL.PLLQ=pllq; //USB/SDIO/随机数产生器等的主PLL分频系数(PLL之后的分频),取值范围:2~15.
当pllm=8,plln=336,pllp=2
系统时钟频率SYSCLK=8 / pllm * plln / pllp=8 / 8 * 336 / 2 =168 MHz
//选中PLL作为系统时钟源并且配置HCLK,PCLK1和PCLK2
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK;//设置系统时钟时钟源为PLL
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1;//AHB分频系数为1
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV4; //APB1分频系数为4
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV2; //APB2分频系数为2
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_5);//同时设置FLASH延时周期为5WS,也就是6个CPU周期。
HCLK = SYSCLK = 168 MHz ;
PCLK1 = SYSCLK / 4 = 42 MHz ; APB1分频系数为4
PCLK2 = SYSCLK / 2 = 86 MHz ; APB2分频系数为2
定时器TIM1由APB2的输出产生
当APB1和APB2的分频系数不为1时,时钟到定时器间会经过一个倍频器
即 定时器TIM1的工作频率:TIM1_CLK = PCLK2 * 2 = 168 MHz
可参考
http://www.elecfans.com/d/760770.html
DAC_HandleTypeDef DAC1_Handler;//DAC句柄
//初始化DAC
void DAC1_Init(void)
{
DAC_ChannelConfTypeDef DACCH1_Config;
DAC1_Handler.Instance=DAC;
HAL_DAC_Init(&DAC1_Handler); //初始化DAC
DACCH1_Config.DAC_Trigger=DAC_TRIGGER_NONE; //不使用触发功能
DACCH1_Config.DAC_OutputBuffer=DAC_OUTPUTBUFFER_DISABLE;//DAC1输出缓冲关闭
HAL_DAC_ConfigChannel(&DAC1_Handler,&DACCH1_Config,DAC_CHANNEL_1);//DAC通道1配置
HAL_DAC_Start(&DAC1_Handler,DAC_CHANNEL_1); //开启DAC通道1
}
//DAC底层驱动,时钟配置,引脚 配置
//此函数会被HAL_DAC_Init()调用
//hdac:DAC句柄
void HAL_DAC_MspInit(DAC_HandleTypeDef* hdac)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_DAC_CLK_ENABLE(); //使能DAC时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_4; //PA4
GPIO_Initure.Mode=GPIO_MODE_ANALOG; //模拟
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
}