本教程使用是(光明谷SUN_STM32mini开发板)
?
免费开发板
?
书接上回:
上回我们用Timer2做了一个定时中断,让LED灯闪烁(STM32F103RCT6开发板M3单片机教程06--定时器中断 ..?)
这回我们用Timer1的CH1 输出PWM做呼吸灯参看原理图(详细到?STM32F103RCT6_SDK开发资料应用手册下载)
?
慢慢变亮又慢慢变暗到灭,如此往复循环灯为呼吸灯也。
上回书得知PA8连接LED阴极,低电平点亮。
参考STM32F103RCT6数据手册, 发现PA8是Timer1的CH1,可以输出PWM
?
上回有云:
大容量的STM32F103xx增强型系列产品包含最多2个高级控制定时器、 4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。
下表比较了高级控制定时器、普通定时器和基本定时器的功能:
?
两个高级控制定时器(TIM1和TIM8)可以被看成是分配到6个通道的三相PWM发生器,它具有带死区插入的互补PWM输出,还可以被当成完整的通用定时器。四个独立的通道可以用于:
● 输入捕获
● 输出比较
● 产生PWM(边缘或中心对齐模式)
● 单脉冲输出
配置为16位标准定时器时,它与TIMx定时器具有相同的功能。配置为16位PWM发生器时,它具有全调制能力(0~100%)。
在调试模式下,计数器可以被冻结,同时PWM输出被禁止,从而切断由这些输出所控制的开关。
很多功能都与标准的TIM定时器相同,内部结构也相同,因此高级控制定时器可以通过定时器链接功能与TIM定时器协同操作,提供同步或事件链接功能。
1、系统初始化(RCC, GPIO)
2、设定初始LED亮度为0,亮度递增
3、高度到100%(全亮时),亮度递减
返回第2步
复制上节的工程源,更改文件夹名
?
修改BSP下Led.c, LED_GPIO_Config函数
更改主GPIO输出模式
原://? ?? ???GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;? ?? ???//设置引脚工作模式为通用推挽输出
改:GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;? ???//PWMout
/*******************************************************************************
* 函数名??: LED_GPIO_Config
* 描述? ? : LED IO配置
* 输入? ? : 无
* 输出? ? : 无
* 返回? ? : 无
* 说明? ? : LED(1~4)的IO口分别是:PB5,PB6,PB7,PB8
*******************************************************************************/
void LED_GPIO_Config(void)
{
? ?? ???GPIO_InitTypeDef??GPIO_InitStructure;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//定义一个GPIO_InitTypeDef类型的GPIO初始化结构体
? ?? ???
? ?? ???RCC_APB2PeriphClockCmd(LED_RCC, ENABLE);? ?? ?? ?? ?? ?? ?? ?? ?//使能GPIOB的外设时钟? ?? ???
? ?? ???
? ?? ???GPIO_InitStructure.GPIO_Pin = LED_ALL;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//选择要初始化的GPIOB引脚(PA5,PA6,PA7,PA8)
? ? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;? ???//PWMout
//? ?? ???GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;? ?? ???//设置引脚工作模式为通用推挽输出? ?? ?? ?? ?? ???
? ?? ???GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;? ?? ???//设置引脚输出最大速率为50MHz
? ?? ???GPIO_Init(LED_PORT, &GPIO_InitStructure);? ?? ?? ?? ?? ?? ?? ?? ?//调用库函数中的GPIO初始化函数,初始化GPIOB中的PA5,PA6,PA7,PA8引脚
? ?
? ?? ???LED_ALL_OFF();? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//关闭ALL_LED? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?
}
修改main.c,??原Timer2定时函数, 修改通用Timer_Init_Config定时中断配置函数(这个也可以不修改不影响功能,为了让源码更接近实际项目代码,提高代码可用性,可移植性,所以这样修改)一般来说TimerPeriod越大越好越定时越精确,但不能超过16bits 最大数65535。
/*******************************************************************************
* 函数名??: Timer_Init_Config
* 描述? ? : Timer初始化配置
* 输入? ? : TIM_TypeDef* TIMx 定时器名 TIM1~8
? ?? ?? ?? ?uint16_t fre_hz 定时器 中断 频率
* 输出? ? : 无
* 返回? ? : 无
* 说明? ? : 无
*******************************************************************************/
void Timer_Init_Config(TIM_TypeDef* TIMx, uint16_t fre_hz)
{
? ?? ???TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
? ?? ???NVIC_InitTypeDef NVIC_InitStructure;
? ?? ???uint16_t TimerPeriod = 0;
? ? uint16_t TimerPrescaler = 71; //1Mhz Timer Conter frequency
? ?
? ? /* Compute the value to be set in ARR regiter to generate signal frequency at Fre_hz */
? ?? ???TimerPeriod = (SystemCoreClock / fre_hz /( TimerPrescaler +1)) - 1;
? ?? ???TIM_TimeBaseStructure.TIM_Period = TimerPeriod; //4999;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? //设置在下一个更新事件装入活动的自动重装载寄存器周期的值(计数到5000为500ms)
? ?? ???TIM_TimeBaseStructure.TIM_Prescaler = TimerPrescaler; //7199;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? //设置用来作为TIMx时钟频率除数的预分频值(10KHz的计数频率) 71
? ?? ???TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;? ?? ?? ?? ?? ? //设置时钟分割:TDTS = TIM_CKD_DIV1
? ?? ???TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;? ?? ???//TIM向上计数模式
? ?? ???TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
? ?? ???/*中断优先级NVIC设置*/
? ?? ???NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//TIM2中断
? ?? ???NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;? ?? ???//先占优先级1级
? ?? ???NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;? ?? ?? ?? ?? ?? ?? ?? ?//从优先级1级
? ?? ???NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//使能IRQ通道
? ?? ???NVIC_Init(&NVIC_InitStructure);? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//初始化NVIC寄存器
? ?? ?? ?
? ?? ???TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE );? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//使能TIMx指定的中断
? ?? ???
? ?? ???TIM_Cmd(TIMx, ENABLE);? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//使能TIMx外设
}? ?? ?? ?
修改main函数
/*******************************************************************************
* 函数名??: main
* 描述? ? : 主函数,用户程序从main函数开始运行
* 输入? ? : 无
* 输出? ? : 无
* 返回值??: int:返回值为一个16位整形数
* 说明? ? : 无
*******************************************************************************/
int main(void)
{
? ? u8 keyVal;
? ? RCC_Configuration();
? ? SysTick_Init_Config();
? ?? ???USART1_Init_Config(115200);//USART1初始化配置
? ? LED_GPIO_Config();
? ? Key_GPIO_Config();
? ?
? ? printf ("*===================================================*\n");
? ? printf ("*??*??Name: Sun STM32 mini Demo Code.? ? *************\n");
? ? printf ("*??* (C) Sunshine Silicon Corporation? ? *************\n");
? ? printf ("*??*??Website: http://www.sunsili.com? ? *************\n");
? ? printf ("*??*? ?E-Mail : fan@sunsili.com? ?? ?? ? *************\n");
? ? printf ("*===================================================*\n");
? ? printf ("* Sun STM32 mini PWM??Demo code .*\n");
? ?
? ? lum = 0;? ? //Start luminance 0
? ? flag = 1;? ?//Up count in
? ? Timer_Ch1_pwm_init(TIM1, 10000, lum);
? ? Timer_Init_Config(TIM2, 100);? ?? ???//Timer2初始化配置 100Hz 中断频率 10ms中断一次
? ?
? ?? ???while (1)
? ?? ???{
? ?? ???if(tim2_tick)
? ?? ???{
? ?? ?? ?? ?tim2_tick = 0;
? ?? ?? ?? ?if(flag)
? ?? ?? ?? ?{
? ?? ?? ?? ?? ?if(++lum >= 100)
? ?? ?? ?? ?? ?{
? ?? ?? ?? ?? ?? ? flag = 0;? ? //Down count in
? ?? ?? ?? ?? ?}
//? ?? ?? ?? ?? ?LED4_ON();
? ?? ?? ?? ?}
? ?? ?? ?? ?else
? ?? ?? ?? ?{? ?? ?? ?? ?? ?
? ?? ?? ?? ?? ? if(!lum)
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ???flag = 1;
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? else
? ?? ?? ?? ?? ? {
? ?? ?? ?? ?? ?? ???lum--;
? ?? ?? ?? ?? ? }
//? ?? ?? ?? ?? ? LED4_OFF();
? ?? ?? ?? ?}
? ?? ?? ?? ?TIM_OC1_set_Pulse(TIM1, 10000, 100-lum);
? ?? ???}
}
增加:
/*******************************************************************************
* 函数名??: Timer_Ch1_pwminit
* 描述? ? : 定时器 Ch1 pwm 初始化
* 输入? ? : TIM_TypeDef* TIMx??定时器名 TIM1~8
? ?? ?? ?? ?uint16_t fre_hz PWM 频率
? ?? ?? ?? ?uint16_t dty 占空比 0-100
* 输出? ? : 无
* 返回? ? : 无
* 说明? ? : 无
*******************************************************************************/
void Timer_Ch1_pwm_init(TIM_TypeDef* TIMx, uint16_t fre_hz, uint16_t dty)
{
? ? TIM_TimeBaseInitTypeDef??TIM_TimeBaseStructure;
? ? TIM_OCInitTypeDef??TIM_OCInitStructure;
? ? uint16_t TimerPeriod = 0;
? ? uint16_t Channel1Pulse = 0;
? ?
? ? /* TIM1 Configuration ---------------------------------------------------
? ?Generate 7 PWM signals with 4 different duty cycles:
? ?TIM1CLK = SystemCoreClock, Prescaler = 0, TIM1 counter clock = SystemCoreClock
? ?SystemCoreClock is set to 72 MHz for Low-density, Medium-density, High-density
? ?and Connectivity line devices and to 24 MHz for Low-Density Value line and
? ?Medium-Density Value line devices
? ?
? ?The objective is to generate 7 PWM signal at 17.57 KHz:
? ???- TIM1_Period = (SystemCoreClock / 17570) - 1
? ?The channel 1 and channel 1N duty cycle is set to 50%
? ?The channel 2 and channel 2N duty cycle is set to 37.5%
? ?The channel 3 and channel 3N duty cycle is set to 25%
? ?The channel 4 duty cycle is set to 12.5%
? ?The Timer pulse is calculated as follows:
? ???- ChannelxPulse = DutyCycle * (TIM1_Period - 1) / 100
??----------------------------------------------------------------------- */
??/* Compute the value to be set in ARR regiter to generate signal frequency at Fre_hz */
??TimerPeriod = (SystemCoreClock / fre_hz ) - 1;
??/* Compute CCR1 value to generate a duty cycle at duty for channel 1 and 1N */
??Channel1Pulse = (uint16_t) (((uint32_t) dty * (TimerPeriod - 1)) / 100);
??/* Compute CCR2 value to generate a duty cycle at 37.5%??for channel 2 and 2N */
//??Channel2Pulse = (uint16_t) (((uint32_t) 375 * (TimerPeriod - 1)) / 1000);
??/* Compute CCR3 value to generate a duty cycle at 25%??for channel 3 and 3N */
//??Channel3Pulse = (uint16_t) (((uint32_t) 25 * (TimerPeriod - 1)) / 100);
??/* Compute CCR4 value to generate a duty cycle at 12.5%??for channel 4 */
//??Channel4Pulse = (uint16_t) (((uint32_t) 125 * (TimerPeriod- 1)) / 1000);
??/* Time Base configuration */
??TIM_TimeBaseStructure.TIM_Prescaler = 0;
??TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
??TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
??TIM_TimeBaseStructure.TIM_ClockDivision = 0;
??TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
??TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
??/* Channel 1, 2,3 and 4 Configuration in PWM mode */
??TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
??TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
??TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
??TIM_OCInitStructure.TIM_Pulse = Channel1Pulse;
??TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
??TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
??TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
??TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
??TIM_OC1Init(TIMx, &TIM_OCInitStructure);
??
??/* TIM1 counter enable */
??TIM_Cmd(TIMx, ENABLE);
??/* TIM1 Main Output Enable */
??TIM_CtrlPWMOutputs(TIMx, ENABLE);
}
/*******************************************************************************
* 函数名??: TIM_OC1_set_Pulse
* 描述? ? : 定时器 Ch1 pwm 脉冲相关设置
* 输入? ? : TIM_TypeDef* TIMx??定时器名 TIM1~8
? ?? ?? ?? ?uint16_t prd??PWD周期
? ?? ?? ?? ?uint16_t dty 占空比 0-100
* 输出? ? : 无
* 返回? ? : 无
* 说明? ? : 无
*******************************************************************************/
void TIM_OC1_set_Pulse(TIM_TypeDef* TIMx, uint16_t fre_hz, uint16_t dty)
{
? ? uint16_t Channel1Pulse = 0;
? ? uint16_t TimerPeriod = 0;
? ?
? ???/* Compute the value to be set in ARR regiter to generate signal frequency at Fre_hz */
? ? TimerPeriod = (SystemCoreClock / fre_hz ) - 1;
? ? /* Compute CCR1 value to generate a duty cycle at duty for channel 1 and 1N */
? ? Channel1Pulse = (uint16_t) (((uint32_t) dty * (TimerPeriod - 1)) / 100);
? ? /* Set the Capture Compare Register value */
? ? TIMx->CCR1 = Channel1Pulse;
}
修改:RCC_Configuration函数
/*******************************************************************************
* 函数名??: RCC_Configuration
* 描述? ? : 设置系统时钟为72MHZ(这个可以根据需要改)
* 输入? ? : 无
* 输出? ? : 无
* 返回值??: 无
* 说明? ? : STM32F107x和STM32F105x系列MCU与STM32F103x系列MCU时钟配置有所不同
*******************************************************************************/
void RCC_Configuration(void)
{
??ErrorStatus HSEStartUpStatus;? ?? ?? ?? ?? ?//外部高速时钟(HSE)的工作状态变量
??
??RCC_DeInit();? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? //将所有与时钟相关的寄存器设置为默认值
??RCC_HSEConfig(RCC_HSE_ON);? ?? ?? ?? ?? ?? ?//启动外部高速时钟HSE
??HSEStartUpStatus = RCC_WaitForHSEStartUp(); //等待外部高速时钟(HSE)稳定
??if(SUCCESS == HSEStartUpStatus)? ?? ?? ?? ? //如果外部高速时钟已经稳定
??{
? ? /* Enable Prefetch Buffer */
? ? FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //Flash设置
? ? /* Flash 2 wait state */
? ? FLASH_SetLatency(FLASH_Latency_2);
? ?
??
? ? RCC_HCLKConfig(RCC_SYSCLK_Div1); //设置AHB时钟等于系统时钟(1分频)/72MHZ
? ? RCC_PCLK2Config(RCC_HCLK_Div1);??//设置APB2时钟和HCLK时钟相等/72MHz(最大为72MHz)
? ? RCC_PCLK1Config(RCC_HCLK_Div2);??//设置APB1时钟是HCLK时钟的2分频/36MHz(最大为36MHz)
? ?
? ? RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); //PLLCLK = 8MHz * 9 = 72 MHz
? ? RCC_PLLCmd(ENABLE); //使能PLL
? ? while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //等待PLL稳定
? ? RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);? ?? ?? ? //设置系统时钟的时钟源为PLL
? ? while(RCC_GetSYSCLKSource() != 0x08);? ?? ?? ?? ?? ?//检查系统的时钟源是否是PLL
? ? RCC_ClockSecuritySystemCmd(ENABLE);? ?? ?? ?? ?? ???//使能系统安全时钟
? ?
? ? /* TIM1 GPIOx clock enable */
? ? RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);
? ? /* TIM2 clock enable */
? ? RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
??}
}
保存全部,编译, 下载。
点运行后,观察运行结果?
运行结果:
串口打印日志信息
?慢慢变亮又慢慢变暗到灭,如此往复循环灯为呼吸灯也。