历史上的今天
今天是:2025年08月01日(星期五)
2019年08月01日 | STM32实战四 定时器和按键
2019-08-01 来源:eefocus
这一章编写定时器,包括定时器基类 Timer 和派生的通用定时器 GeneralTimer。基类对定时器参数进行封装,通用定时器封装一些定时应用,对应PLC的一些功能,包括:
1ms定时中断
100个32位数字时间继电器,最小1ms,最大0xffffffff,大约50天。
一个高精度回调函数,微秒级误差,最小定时间隔1ms。
按键抖动和干扰过滤,并产生按键上升沿和下降沿。
代码中有详细的说明,这里只解释几个知识点,其它文档介绍按键防抖和延时的时候一般都是死循环,官方文档也是这么用,如果有很多按键和延时就会一个一个等,效率很低。我这里用了另外一种高效的方法,就是模仿时间继电器,100个计数器同时工作,直到计数为0时执行对应操作,这样主循环没有等待,循环周期只有几十微秒,能进行高精度实时控制,具体方法下一章中介绍,这里先做好基础。
按键滤波后动作会有一定的延时,大约4ms加主循环周期,屏蔽了高频信号,对高速信号不适用。
GeneralTimer中的100个定时器u32 m_t[100]占用很多栈空间,导至程序不运行,要增加栈空间,方法是增大 startup_stm32f10x_hd.s 文件中的 Stack_Size 项,我这里加到了 0x0800,也就是2k字节,原来是0x0200,512字节。其它不多说了,看代码:
Timer.h
#ifndef __TIMER__
#define __TIMER__
extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段
#pragma diag_remark 368 //消除 warning: #368-D: class " #include "stm32f10x.h" #include "stm32f10x_tim.h" #pragma diag_default 368 // 恢复368号警告 } // TIMx 1ms定时 class Timer { // Construction public: Timer(TIM_TypeDef * TIMx); // tim:TIM1-8 // Properties public: TIM_TypeDef * m_pTIMx; // 定时器指针 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; private: // Methods public: inline void timInit(void); // 初始化定时 inline void nvicInit(void); // 初始化优先级 void enableInterrupt(void); // 使能中断 virtual void onTimer(void); // 中断,返回1溢出,0不溢出 // Overwrite public: }; // 中断处理 extern "C" { // 兼容C void TIM2_IRQHandler(void); void TIM3_IRQHandler(void); void TIM4_IRQHandler(void); void TIM5_IRQHandler(void); void TIM6_IRQHandler(void); void TIM7_IRQHandler(void); void TIM8_UP_IRQHandler(void); void TIM1_UP_IRQHandler(void); } #endif Timer.cpp /** ****************************************************************************** * @file Timer.cpp * @author Mr. Hu * @version V1.0.0 STM32F103VET6 * @date 05/19/2019 * @brief 定时器基类 * ****************************************************************************** * @remarks * 定时器基类,默认不开启中断,可用类方法enableInterrupt使能中断 * 默认定时周期1ms * * 参考资料: * STM32 Keil C++编写单片机程序 * https://www.cnblogs.com/yeshuimaowei/p/6949642.html * 利用基本定时器进行精确延时 * https://blog.csdn.net/sahpah/article/details/38545637 * TIM1和8是高级定时器,配置方法不同 * https://www.cnblogs.com/pertor/p/9488813.html */ /* Includes ------------------------------------------------------------------*/ extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段 #pragma diag_remark 368 //消除 warning: #368-D: class " #include "stm32f10x_tim.h" #include "misc.h" #pragma diag_default 368 // 恢复368号警告 } #include "Timer.h" // 全局指针,每个定时器对应一个指针,不能合并 Timer * pTim2 = 0; Timer * pTim3 = 0; Timer * pTim4 = 0; Timer * pTim5 = 0; Timer * pTim6 = 0; Timer * pTim7 = 0; Timer * pTim8 = 0; Timer * pTim1 = 0; /** * @date 05/19/2019 * @brief 初始化定时器基类. * @param m_pTIMx:TIM1-8 * @retval None */ Timer::Timer(TIM_TypeDef * pTIMx) { assert_param(IS_TIM_ALL_PERIPH(TIMx)); // 保存变量 m_pTIMx = pTIMx; if(pTIMx == TIM2) pTim2 = this; else if(pTIMx == TIM3) pTim3 = this; else if(pTIMx == TIM4) pTim4 = this; else if(pTIMx == TIM5) pTim5 = this; else if(pTIMx == TIM6) pTim6 = this; else if(pTIMx == TIM7) pTim7 = this; else if(pTIMx == TIM8) pTim8 = this; else if(pTIMx == TIM1) pTim1 = this; //else // ?? 异常 nvicInit(); timInit(); } /** * @date 05/19/2019 * @brief 初始化定时器,周期1ms. * @param None * @retval None */ void Timer::timInit(void) { if(m_pTIMx == TIM2) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // TIM2-7 时钟使能 else if(m_pTIMx == TIM3) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); else if(m_pTIMx == TIM4) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); else if(m_pTIMx == TIM5) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); else if(m_pTIMx == TIM6) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); else if(m_pTIMx == TIM7) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE); else if(m_pTIMx == TIM8) RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); // TIM1和8 时钟使能 else if(m_pTIMx == TIM1) RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); TIM_DeInit(m_pTIMx); // 使用缺省值初始化TIM外设寄存器 //TIM_TimeBaseStructInit( &TIM_TimeBaseStructure ); // 5项都独立初始化了,不用这个 TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 自动重装载寄存器值,软件仿真用1000-1,软件仿真很慢,可用100-1 TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 时钟预分频数为72-1,定时周期计算方法 72M/72/1000 = 1ms TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //采样分频倍数1,未明该语句作用。 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //上升模式 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //高级定时器1和8是用定时器功能配置这个才可以是正常的计数频率一开始的72Mhz 值得注意的地方 TIM_TimeBaseInit(m_pTIMx, &TIM_TimeBaseStructure); TIM_ClearFlag(m_pTIMx, TIM_FLAG_Update); //清除更新标志位 //TIM_ITConfig(m_pTIMx, TIM_IT_Update, ENABLE); //使能中断, //在这里使能中断不好,会产生很多没用的中断,所有初始化完成后 TIM_Cmd(m_pTIMx, ENABLE); //使能TIM定时器 } /** * @date 05/19/2019 * @brief 初始化优先级 // ?? 需要完善 * @param None * @retval None */ void Timer::nvicInit(void) { NVIC_SetPriorityGrouping(NVIC_PriorityGroup_0); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = //TIMx 中断 // ?? 越界 m_pTIMx != TIM2 ? m_pTIMx != TIM3 ? m_pTIMx != TIM4 ? m_pTIMx != TIM5 ? m_pTIMx != TIM6 ? m_pTIMx != TIM7 ? m_pTIMx != TIM8 ? m_pTIMx != TIM1 ? 0 : TIM1_UP_IRQn : TIM8_UP_IRQn : TIM7_IRQn : TIM6_IRQn : TIM5_IRQn : TIM4_IRQn : TIM3_IRQn : TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级 1 级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级 3 级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道被使能 NVIC_Init(&NVIC_InitStructure); //初始化 NVIC 寄存器 } /** * @date 05/19/2019 * @brief 中断 * @param None * @retval None */ void Timer::onTimer(void) { // ?? 下面的判断在大部分示例中都有,实测无用,先注释 //if(TIM_GetITStatus(m_tim,TIM_IT_Update) == RESET) //溢出中断 // return; TIM_ClearITPendingBit(m_pTIMx, TIM_IT_Update);//清除溢出标志 } /** * @date 05/19/2019 * @brief 使能中断,放到初始化和配制以后,否则会产生好多没用的中断 * @param None * @retval None */ void Timer::enableInterrupt(void) { TIM_ClearITPendingBit(m_pTIMx, TIM_IT_Update);//清除溢出标志,否则会产生一个中断 TIM_ITConfig(m_pTIMx, TIM_IT_Update, ENABLE);//使能中断, 不能先使能中断,会产生很多没用的中断 } // 各定时器中断入口,调用各自的onTimer(); void TIM2_IRQHandler(void) { pTim2->onTimer(); } void TIM3_IRQHandler(void) { pTim3->onTimer(); } void TIM4_IRQHandler(void) { pTim4->onTimer(); } void TIM5_IRQHandler(void) { pTim5->onTimer(); } void TIM6_IRQHandler(void) { pTim6->onTimer(); } void TIM7_IRQHandler(void) { pTim7->onTimer(); } // TIM1和8是高级定时器 void TIM8_UP_IRQHandler(void) { pTim8->onTimer(); } void TIM1_UP_IRQHandler(void) { pTim1->onTimer(); } GeneralTimer.h #ifndef __GENERALTIMER__ #define __GENERALTIMER__ #pragma anon_unions // 允许使用匿名结构 #include "Timer.h" union INx // 记录输入口状态,按位分割 { u16 state; // 状态字 struct { u8 count : 8; // 转换计数,低8位 u8 enable : 1; // 前次电平,第8位,从0开始 u8 level : 1; // 当前电平,第9位 u8 up : 1; // 上升沿,第10位 u8 down : 1; // 下降沿,第11位 u8 reserve: 4; // 预留 }; }; // 通用定时器 class GeneralTimer : public Timer { // Construction public: GeneralTimer(TIM_TypeDef * m_pTIMx); // Properties public: u32 m_t[100]; // 100个定时器,要修改栈空间,startup_stm32f10x_hd.s Stack_Size EQU 0x400 ;//0x00000200 // 栈大小http://m.elecfans.com/article/588038.html INx ina[16], inb[16], inc[16], ind[16], ine[16]; private: u16 m_nFT1; u16 m_nFT2; void (*m_pBack)(void); // Methods public: virtual void onTimer(void); // 中断,返回1溢出,0不溢出 void setCallback(void (*pBack)(void), u16 nCount); void loop(void); // 主循环中调用,必需在循环的最开始 inline void addCount( GPIO_TypeDef* GPIOx, u16 nPin, INx & inx ); inline void upDown( INx & inx ); // Overwrite public: }; #endif GeneralTimer.cpp /** ****************************************************************************** * @file GeneralTimer.cpp * @author Mr. Hu * @version V1.0.0 STM32F103VET6 * @date 06/62/2019 * @brief 通用定时器 * ****************************************************************************** * @remarks * 通用定时器,启用中断,包括3大功能中断方式 * 1. 100个时间继电器,单位ms,最大0xffffffff,大约50天。 * 2. 输入信号数字滤波,方法是,连续4ms(四次采样),信号反向不变,输入信号改变,并产 * 生一个主循环(main中的循环)周期的上升或下降沿信号。作用是防止抖动和干扰,高 * 速信号不能用这个滤波。 * 3. 高精度回调函数,回调周期最小1ms,最大65535ms,精度us * * 参考资料: * STM32 Keil C++编写单片机程序 * https://www.cnblogs.com/yeshuimaowei/p/6949642.html * 利用基本定时器进行精确延时 * https://blog.csdn.net/sahpah/article/details/38545637 * TIM1和8是高级定时器,配置方法不同 * https://www.cnblogs.com/pertor/p/9488813.html */ /* Includes ------------------------------------------------------------------*/ extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段 #pragma diag_remark 368 //消除 warning: #368-D: class " #include "stm32f10x_tim.h" #pragma diag_default 368 // 恢复368号警告 } #include "GeneralTimer.h" #define COUNT 0x4 // 二进制1位 /** * @date 06/02/2019 * @brief 通用定时器,从Timer继承 * @param m_pTIMx:TIM1-8 * @retval None */ GeneralTimer::GeneralTimer(TIM_TypeDef * m_pTIMx) : Timer(m_pTIMx) , m_nFT1(0) , m_nFT2(0) , m_pBack(0) { // 初始化100个时间继电器 for(int i = 0; i < sizeof(m_t)/sizeof(m_t[0]); i++) m_t[i] = 0; // 按键滤波,防止抖动和干扰 for( u8 i = 0; i < 16; i++ ) { ina[i].state = 0x0200; // 0x0200表示ina[i].level = 1,默认上拉 inb[i].state = 0x0200; inc[i].state = 0x0200; ind[i].state = 0x0200; ine[i].state = 0x0200; } enableInterrupt(); // 允许中断 } /** * @date 06/02/2019
上一篇:STM32实战二 新建工程
史海拾趣
|
《社区大讲堂》DO-254中的高设计可靠性的逻辑综合(七)--支持逻辑等效型检查 设计可靠性在DO-254的A级和B级设计中尤其重要。. 附录B中对有这样的描述:“当设计可靠性级别增加后, 设计方法需要能够验证被测设计满足安全性要求, 这些要求有可能有重合的部分,需要有层次化的设计可靠性设计方法。 在任何设计过程中,如 ...… 查看全部问答> |
|
我是按照这个网页一步一步作的:http://www.pcwiki.net/ht/view/cps-4/id-20978 前面一切都调试成功,作到上面页面的Target | Attach步后,,出现“download Runtime Image to CE Device \" 然后进度条就一直没有变,请问这个下载很 ...… 查看全部问答> |
|
这是WINCE帮助文档上的介绍: LONG RegReplaceKey( HKEY hKey, LPCTSTR lpSubKey, LPCTSTR lpNewFile, LPCTSTR lpOldFile ); hKey [in] The hKey parameter must be HKEY_LOCAL_MACHINE ...… 查看全部问答> |
|
本人是计算机专业毕业,对java,php,delphi都有相当的工作经验,学过一定时间的伟福,但它的接口没有RAM丰富,现在想学一下RAM,请问一下RAM是在windows还是linux下运行,还有就是调度软件用什么,要不要仿真器.… 查看全部问答> |
|
M0 新唐CM0+MDK-ARM入门开发过程简介之平台的建立 一 平台资源下载 1.Mdk4.12的下载因为是最新发布的芯片所以现在只有mdk4.11和4.12支持本系列芯片。 下载地址 在http://www.mcu123.com/news/Soft/ShowSoftDown.asp?UrlID=3&SoftID=529上右击“下载地址:” ...… 查看全部问答> |
|
3G模块:EM770w 直接用AT命令控制,已实现PPP连接(因为获得网关分配的私有IP,我认为,不知道对否)。在这种情况下如何进行UDP通信。因为打算把EM770W放在一块开发板上,所以希望有高手能提供一些关于AT命令或者更底层的意见。 这个EM770W完全不 ...… 查看全部问答> |




