STM32F103RCT6开发板M3单片机教程07-TIMER1CH1输出 PWM做LED呼吸灯

概述

本教程使用是(光明谷SUN_STM32mini开发板
 



免费开发板
 

在谷动谷力社区注册用户,打卡,发帖求助都可以获取积分,当然最主要是发原创应用文档奖励更多积分.
(可用积分换取,真的不用钱,开发板免费玩):STM32F103RCT6开发板M3单片机核芯小系统板学习板 ...
已经购买用的,可以注册用户,打卡,发帖求助都可以获取积分,当然最主要是发原创应用文档奖励更多积分.用积分换钱,返还您的买开发板钱
详细参阅
[推荐]STM32F103RCT6开发板M3单片机最小系统板学习板 带USB JTAG...

 
资料下载
STM32F103RCT6_SDK开发资料应用手册下载


书接上回:
上回我们用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)

两个高级控制定时器(TIM1TIM8)可以被看成是分配到6个通道的三相PWM发生器,它具有带死区插入的互补PWM输出,还可以被当成完整的通用定时器。四个独立的通道可以用于:
● 输入捕获
● 输出比较
● 产生PWM(边缘或中心对齐模式)
● 单脉冲输出
配置为16位标准定时器时,它与TIMx定时器具有相同的功能。配置为16PWM发生器时,它具有全调制能力(0~100%)
在调试模式下,计数器可以被冻结,同时PWM输出被禁止,从而切断由这些输出所控制的开关。
很多功能都与标准的TIM定时器相同,内部结构也相同,因此高级控制定时器可以通过定时器链接功能与TIM定时器协同操作,提供同步或事件链接功能。


程序流程

1、系统初始化(RCC, GPIO)
2、设定初始LED亮度为0,亮度递增
3、高度到100%(全亮时),亮度递减
返回第2步



开发码码

复制上节的工程源,更改文件夹名
 

GPIO配置--PWM输出模式

修改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                                          
}


定时器配置--PWM模式

修改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);
  }
}

编译调试

编译


保存全部,编译, 下载。

调试

点运行后,观察运行结果 
运行结果:

串口打印日志信息

 慢慢变亮又慢慢变暗到灭,如此往复循环灯为呼吸灯也。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/314859.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是 CAS

程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一波电子书籍资料,包含《Effective Java中文版 第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》&…

C语言快速排序——qsort函数

上面的是我们标准C语言库里面对qsort函数的介绍,我们先来从排序说起: 这就不得不提出编程中最最基础的排序算法---冒泡排序 对于一个任意的无序数列,我们如果想要把他排成顺序数列的话,我们就可以让每一项跟后面的一项去比较&…

基于汽车胎压检测及温度预警

课题简介 汽车胎压监测系统可分为两种:一种是间接式胎压监测系统,是通过轮胎的转速差来判断轮胎是否异常;另一种是直接式胎压监测系统,通过在轮胎里面加装四个胎压监测传感器,在汽车静止或者行驶过程中对轮胎气压和温…

轻量化的yolov8部署到安卓Android手机端

一、pytorch环境配置和yolov8源码安装 首先在电脑上需要配置好pytorch环境,和yolov8源码的下载 然后针对yolov8做自己的轻量化改进 二、下载Android Studio和ncnn-android-yolov8 1. Android Studio官网链接: 下载 Android Studio 和应用工具 - And…

语义补全任务2023年论文总结

一、3D Semantic Scene Completion: a Survey 语义场景补全SSC旨在联合估计出一个场景完整的几何和语义信息,假设只需要部分稀疏输入数据。 1、数据集 2、场景表示 Point Cloud,点云是一种方便的、记忆高效的表示方法,它将三维连续世界中的…

图灵机:计算机科学的奠基之作

图灵机的概念由英国数学家阿兰图灵在1936年提出,这个时期正是计算机科学的黎明时分。那个时候,人们还在使用机械计算器进行计算,而且这些计算器的功能都非常有限。 图灵提出这个概念的初衷,是为了解决所谓的“判定问题”&#xf…

关于react-native-reanimated 3.6.1在react native debugger报错问题

ExceptionsManager.js:158 Error: [Reanimated] UpdatePropsManager is not available on non-native platform. 在node_module下找到找到相关文件,注释掉相关代码 然后打补丁放在自己的项目下,关于打补丁在博客主页,自行查看讲解

番外篇-如何开发智能合约入门

今天咱们聊聊如何开发智能合约,非常入门的分享~ 1. 如何开发智能合约 1.1. 基本流程 & 主流工具 1.1.1. 编写合约代码 Solidity仍然是一骑绝尘(EVM)Vyper是不太活跃语言,python语法(EVM)Rust不能应…

【pwn】cmcc_simplerop --rop链的构造

程序保护情况检查 32位程序,堆栈不可执行 主函数: 左边又是一堆函数,file看一下 发现是静态链接,那ret2libc不用考虑了,接着看一下有没有int 80 那可以考虑利用rop链调用execve函数,用系统调用的函数参数是…

想做鸿蒙开发应该学会哪些知识?

鸿蒙开发学习是一项探索性的工作,旨在开发一个全场景分布式操作系统,覆盖所有设备,让消费者能够更方便、更直观地使用各种设备。 鸿蒙系统定位为面向未来、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分…

Mysql时间差8小时解决方案

目录 1. MySQL 本身的问题1-1. 验证MySQL时间1-2. 修改Mysql时区配置文件修改Mysql时区SQL修改Mysql时区 2.JDBC 连接的问题3. 返回 JSON 时间不对 在开发中,有可能会遇到这种情况: 插入数据库中的时间时正常。但是将时间传到前端页面上显示时&#xff…

Docker安装Elesticsearch7详细步骤

​ 1、创建安装目录 mkdir -p /usr/local/docker/es-docker 2、配置虚拟内存 如果不配置,后面启动es会报错。 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144] 配置如下 vi /etc/sysctl.conf vm.max_map_coun…

光伏行业常用到的专业名词有哪些?都是什么意思?

光伏行业作为新能源领域的重要组成部分,涉及许多专业名词。本文将介绍一些常用的专业名词及其含义。 光伏电池:光伏电池是光伏发电系统的核心部件,能够将太阳能转换成直流电能。它是通过光生电效应工作的。 光伏组件:光伏组件是…

【vue3】vue3的应用实例是什么和怎么使用

简言 每个 Vue 应用都是通过 createApp 函数创建一个新的应用实例。 应用实例指的就是createApp的返回值,它是一个对象,里面包含了多个vue相关的属性,例如component(组件)、directive(指令)use…

自动化测试数据校验神器!

在做接口自动化测试时,经常需要从接口响应返回体中提取指定数据进行断言校验。 今天给大家推荐一款json数据提取神器: jsonpath jsonpath和常规的json有哪些区别呢?在Python中,json是用于处理JSON数据的内置模块,而jsonpath是用…

代码随想录 516. 最长回文子序列

题目 给你一个字符串 s ,找出其中最长的回文子序列,并返回该序列的长度。 子序列定义为:不改变剩余字符顺序的情况下,删除某些字符或者不删除任何字符形成的一个序列。 示例 1: 输入:s “bbbab” 输出&…

Jmeter接口自动化03-JMeter的常用核心组件

p03 高清B站视频链接 由于JMeter涉及的组件数目很多,据不完全统计至少有110个,而其实只需要掌握20%的组件就可以完成80%甚至更多的日常工作了,所以接下来我们重点剖析使用最频繁的核心组件,如下图所示。只需要优先掌握这10个左右…

百家大吉·夕阳关爱——昌岗街微型养老博览会

居民热情参与博览会 为让长者了解及选择适合自己的养老服务,昌岗街在2023年12月27日开展以“百家大吉夕阳关爱”为主题的昌岗街微型养老服务公益博览会活动,通过搭建养老服务机构供需服务平台,拓宽社区长者了解正规养老服务机构的渠道&#…

部署ATS(Apache Traffic Server)和Nginx正向代理服务性能对比

部署ATS(Apache Traffic Server)和Nginx正向代理服务&性能对比 1. 正向代理的用途2. ATS(Apache Traffic Server)正向代理服务器部署3. Nginx正向代理服务器部署4. 性能对比 1. 正向代理的用途 正向代理一般是用于内部网络出去,反向代理一…

三、GCC编译:链接

代码准备 main.c extern int shared; extern void func(int *a, int *b); int main(){int a 100;func(&a, &shared);return 0; }func.c int shared 1; int tmp 0; void func(int *a, int *b){tmp *a;*a *b;*b tmp; }静态链接 编译 gcc -static -fno-stack-p…