写到第五章,终于有可以看见的结果了。不过磨刀不误砍柴功。正因为前面的基础,才有今天的成果,而且有一定的实用价值。封装一个BoardLED类,主要功能是利用板载LED显示数据,类似于 Morse code 电报码,以点亮时间的长短表示二进制数据的0和1,最多4位二进制,十进制15,可以扩展到更多。下面的程序显示主循环周期时间,单位1us,实际上可以用来显示设置运行状态。购买或开发不同的开发板,LED对应的脚号不同,修改程序对应的IO号即可。
BoardLED.h 代码中用到了前几章的封装类,需要前面的原程序,全部做完以后做一个总的封包,上传到资源库中。
#ifndef __BOARDLED__
#define __BOARDLED__
extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x.h"
#pragma diag_default 368 // 恢复368号警告
}
#include "GeneralTimer.h"
#include "IO.h"
class BoardLED : public IO
{
// Construction
public:
BoardLED(GeneralTimer * pTimer);
// Properties
public:
GeneralTimer * m_pTimer; // 定时器
u16 m_nNum; // 要显示的数
u16 m_nCurrent; // 当前显示的位
private:
// Methods
public:
void showNumber(void); // 四位二进制形式显示m_nNum
void showLED(u16 nPin, u8 nOn); // 开关LED
// Overwrite
public:
};
#endif
BoardLED.cpp
/**
******************************************************************************
* @file BoardLED.cpp
* @author Mr. Hu
* @version V1.0.0 STM32F103VET6
* @date 05/20/2019
* @brief 板载LED
* @IO
* PB13 板载LED2,不同的开发板,端口不同,做相应调整
* PE14 板载LED3
* m_t[0] 定时器m_pTimer中的计数1
******************************************************************************
* @remarks
* 从IO类继承,初始化对应端口
* LED2长短编码形式显示二进制数据,短是0,长是1
* 只显示m_nNum的低四位
*/
/* Includes ------------------------------------------------------------------*/
extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x_tim.h"
#pragma diag_default 368 // 恢复368号警告
}
#include "GeneralTimer.h" // 通用定时器
#include "BoardLED.h"
/**
* @date 05/20/2019
* @brief 板载LED类
: * IO(GPIOB, GPIO_Pin_13 | GPIO_Pin_14, GPIO_Mode_Out_PP, 1)
* 初始化板载LED对应端口,不同的开发板,端口不同,做相应调整,
* 最后一个参数1表示初始化为上拉
* @param pTimer,指向通用定时器类对象
* @retval None
*/
BoardLED::BoardLED(GeneralTimer * pTimer)
: IO(GPIOB, GPIO_Pin_13 | GPIO_Pin_14, GPIO_Mode_Out_PP, 1) // 初始化板载LED对应端口,不同的开发板,端口不同,做相应调整,最后一个参数1表示初始化为上拉
, m_pTimer(pTimer) // 保存定时器
, m_nNum(0) // 要显示的数,0-15
, m_nCurrent(0x8) // 当前显示的位,最多4位
{
}
/**
* @date 05/20/2019
* @brief 以二进制形式显示m_nNum指定的数据
: * LED2点亮时间的长短表示二进制位,短是0,长是1,最大二进制数1111,十进制15。
* @param None
* @retval None
*/
void BoardLED::showNumber(void)
{
u32 & tim = m_pTimer->m_t[0]; // 定时时间
if(tim)
return; // 只有计数到0才显示
u8 level = GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_13);
if(level) // 当前是高电平,LED不亮
{
GPIO_ResetBits(GPIOB, GPIO_Pin_13); // 设置高电平,点亮
tim = (m_nCurrent & m_nNum) ? 500 : 5; // 当前二进制位是1,显示500ms,如果二进制位是0显示200ms秒
m_nCurrent >>= 1; // 移到下一位
}
else // 当前是低电平,LED亮
{
GPIO_SetBits(GPIOB, GPIO_Pin_13); // 设置高电平,熄灭
if(!m_nCurrent) // 数据显示完毕,暗1.5秒
{
tim = 1500; // 延时1500ms
m_nCurrent = 0x8; // 初始化当前位到最高位
}
else // 二个二进制位中间,暗500ms
tim = 500;
}
}
/**
* @date 05/20/2019
* @brief 板载LED点亮和熄灭
* @param nPin,端口号,PB13 和/或 PB14
* @param nOn,0灭,1亮
* @retval None
*/
void BoardLED::showLED(u16 nPin, u8 nOn)
{
if( nOn )
GPIO_ResetBits(GPIOB, nPin); // 点亮
else
GPIO_SetBits(GPIOB, nPin); // 熄灭
}
Main.h 文件中还没有实际内容
#ifndef __MAIN__
#define __MAIN__
extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x.h"
#pragma diag_default 368 // 恢复368号警告
}
#endif
Main.cpp 主入口程序,所列端口有些是以后用的。程序实现两个功能,一是计算主循环周期,二是设置两个板载按键,按下时LED3点亮,并具有防抖动和抗干扰功能。
/**
******************************************************************************
* @file Main.cpp
* @author Mr. Hu
* @version V1.0.0 STM32F103VET6
* @date 05/18/2019
* @brief 程序入口
* @io
* TIM3 PWM
* TIM4 Encode
* TIM7 通用定时器
* ADC1 ADC
* DAC1
* DAC2
*
* PA0-PA3 ADC
* PA4 DAC1输出
* PA5 DAC2输出
* PA6 PWM1
* PA7 PWM2
* PA9 板载串口
* PA10 板载串口
* PA13 板载JLINK占用
* PA14 板载JLINK占用
* PA15 板载JLINK占用
*
* PB1 板载SW2
* PB3 板载JLINK占用
* PB4 板载JLINK占用,部分映像的通道1不能用,所以用了没有得映像
* PB5 PWM
* PB6 编码器 A
* PB7 编码器 B
* PB8 板载CAN
* PB9 板载CAN
* PB10 板载RS485
* PB11 板载RS485
* PB13 板载LED2
* PB14 板载LED3
* PB15 板载SW3
*
* PC4 板载RS485
* PC5 板载RS485
******************************************************************************
* @remarks
*
*/
extern "C" { // 兼容C,按C语言编译,Keil5中的包含文件已经加入了C++兼容,不用再加这一段
#pragma diag_remark 368 //消除 warning: #368-D: class "<unnamed>" defines no constructor to initialize the following:
#include "stm32f10x_tim.h"
#include "stm32f10x_dac.h"
#pragma diag_default 368 // 恢复368号警告
}
#include "stm32f10x_adc.h"
#include "IO.h"
#include "Timer.h"
#include "GeneralTimer.h"
#include "BoardLED.h"
#include "Main.h"
/**
* @date 05/18/2019
* @brief 主入口,主循环
* 如果不正常运行,可能是栈设置不够 startup_stm32f10x_hd.s Stack_Size EQU 0x600
* @param None
* @retval None
*/
int main(void)
{
SystemInit(); // 配置系统时钟为72M
GeneralTimer tim(TIM2); // 通用定时器,实际用TIM7,不占用IO,但软件仿真只有1-4,所以选2
BoardLED boardLED( &tim ); // 板载LED
// 板载按键,PB1 SW2, PB2 SW3,不同的板子不一样。
IO key(GPIOB, GPIO_Pin_1 | GPIO_Pin_15, GPIO_Mode_IPU, 2); // GPIOx, nPin, GPIO_Mode_IPU 上拉, 2 输入时无效
// 使能按键滤波
//tim.inb[1].level = 1; // SW2 PB1 上拉
tim.inb[1].enable = 1; // SW2 PB1 使能
//tim.inb[15].level = 1; // SW3 PB15 上拉
tim.inb[15].enable = 1; // SW3 PB15
u32 loopCount = 0; // 主循环计数
while(1)
{
tim.loop(); // 必须放在主循环的第一行,按键滤波和上下沿微分。
// LED
// 测试时间
loopCount++;
if( !tim.m_t[2] ) // 定时器2
{
tim.m_t[2] = 1000; // 延时1000ms
boardLED.m_nNum = 1000 * 1000 / loopCount; // 计算循环周期,1000*1000对应周期单位是1us,100*1000是10us,以此类推。
if( boardLED.m_nNum > 0xf )
boardLED.m_nNum = 0xf; // 大于15时,显示15
loopCount = 0;
}
boardLED.showNumber(); // 显示四位二进制boardLED.m_nNum,用了m_t[0]
// 开关LED
if( tim.inb[1].down | tim.inb[15].down ) // 两个板载开关的下降沿
{
boardLED.showLED(GPIO_Pin_14, 1); // 点亮LED3
}
else if( tim.inb[1].up | tim.inb[15].up ) // 两个板载开关的上升沿
{
boardLED.showLED(GPIO_Pin_14, 0); // 熄灭LED3
}
}
}
把实战三、四、五的源程序加在一起,编译下载到单片机中,会看到LED【长短长短】有节奏的显示二进制1010,说明主循环周期是6us,软件仿真示波器显示如下,低电平是点亮。
不同的开发板,数据会不同,如果全部是0或全部是1表示溢出,修改Main.cpp中的量程,【1000*1000对应周期单位是1us,100*1000是10us,以此类推。】,如果都是0,这个数加大,否则减小。这种显示方式还有一个更好的用途就是显示错误代码,0表示正常,1-15表示各种异常,当然还可以用作其它用途。
本代码引用的其它程序:
STM32实战之三 C++ IO.cpp STM32实战之四 定时器和按键
敬请期待下一章 《STM32实战之六 PWM》