学习硬件测试04:触摸按键+PWM 驱动蜂鸣器+数码管(P62~P67、P71、P72)

一、触摸按键

1.1理论讲解

1.1.1实验现象

  1. 触摸按键 1 单击与长按,控制 LED1;
  2. 触摸按键 2 单击与长按,控制 LED2;
  3. 触摸按键 3 单击与长按,控制 LED3;
  4. 触摸按键 4 单击与长按,控制继电器;

1.1.2硬件电路

是原理图上触摸按键、继电器、LED 灯三部分电路。

注意;按键一般为高电平,按下后为低电平,在配置时就要注意是下降沿触发。

1.1.3外部中断

1.1.4HAL库函数

第一行:读管脚状态,要么低电平,要么高电平。

第二行:写管脚状态,写高或者写低。

第三行:选择一个管脚,状态取反。

第四行:锁定管脚,进行保护状态,不能写了。

第五行:中断处理函数,软件自动生成的,后续讲解。

第六行:外部中断的回调函数,其中的变量是指示具体管脚,是具体管脚的回调函数。(如PA~PA15共用一个外部中断,如上图,共用一个回调函数,通过变量去判断具体是哪一个管脚引起的中断)

1.2初始化

1.2.1继电器初始化

推挽输出,默认低电平,使其刚开始处于断开的模式。

1.2.2触摸按键初始化

一般为高电平,按下后为低电平,故配置成下降沿触发。

(如果配上升沿触发,单击按键松开才触发,长按不触发;如果配置成均触发,则单击触发两次,所以触发方式的配置和硬件紧密相连)

注意不是配置成输入模式,而是配置成中断模式,后面软件编写也是启动中断的功能。

注意当按键 1 配置成中断时,与其共使用一个外部中断的其他引脚配置成的中断就会自动消失 

1.2.3配置按键的中断

1.3编程

在 main.c 中代码都是一样的,但是代码调用的代码是不一样的,不论是软件自动生成的初始化代码,还是我们自己编写的代码。

MyInit.Peripheral_Set() 自定义初始化代码如下:

static void Peripheral_Set()
{
	printf("----此程序实现触摸按键单击与长按功能----\r\n");
	printf("Initialization completed, system startup!\r\n");
	printf("Software version is V%.1f\r\n\r\n",SoftWare_Version);
	
	printf("等待触摸按键:\r\n\r\n");
}

说明:

  1. 代码比较灵活,长按的时间可以自由设定。
  2. 单击的时间也可以自由设定,可以不是10ms检查一次,可以是5ms检查一次,用户就不用等待那么长的时间。
  3. 本测试中按键式中断,并且是不影响其他中断的运行或者被影响,当有别的中断并不会影响该中断检查是不是长按/ 单击的程序。
  4. 注意上面说的别的中断,以及在其他任何项目中的一般中断,都要遵循“中断快进快出”的原则,运行几十微秒~几百微秒,问题都不大。

1.4实验现象

单击翻转 LED 灯的状态,长按 ≥2S 翻转两次 LED 灯的状态,两次间隔 200ms。

触摸按键

二、PWM 驱动蜂鸣器

2.1理论讲解

2.1.1程序功能

上电后,无源蜂鸣器发出声音,通过触摸按键关闭或打开蜂鸣器。

2.1.2无源蜂鸣器

无源蜂鸣器没有振动源,驱动需要 PWM 波来驱动,通过改变 PWM 波的频率来改变蜂鸣器的声音。

2.1.3 PWM

英文全称 Pulse-Width Modulation,脉冲宽度调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,通过调占空比调节光的强度、声音、电机等。本次是通过通用定时器进行频率调节。

2.1.4通用定时器 TIMx

本次是通过通用定时器来调节 PWM 的频率,TIMx 器是一个通过可编程预分频器驱动的 16 位自动装载计数器构成。它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)。使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整,每个定时器都是完全独立的,没有互相共享任何资源,可以一起同步操作,包含:

  • 计数器寄存器(TIMx_CNT)
  • 预分频器寄存器 (TIMx_PSC)
  • 自动装载寄存器 (TIMx_ARR)

2.1.5计数模式

在向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。这样的计数方式更加灵活,在计数中间时可以和另一个设定的值进行比较,进行其他需要的操作。

2.1.6 PWM模式

脉冲宽度调制模式可以产生一个由 TIMx_ARR 寄存器确定频率由TIMx_CCRx寄存器确定占空 比的信号

2.1.7HAL库函数

这些是定时器关于PWM的函数:

  1. 上面4个是:初始化函数,不用我们写
  2. 第4个:DMA功能,没用到
  3. 第3个:中断,没用到
  4. 第2个:启动、停止输出功能,后续就是通过触摸按键调用这两个函数打开或者关闭报警。

2.2软件初始化

2.2.1 框1

蜂鸣器的引脚为 PA8 ,在 Cubemx 中 PA8 只能设置成为定时器 1,定时器 1 是高级定时器,具有上面所讲的通用定时器的功能,我们也只使用通用定时器的功能,其他的功能不展开讲解和使用。

2.2.2 框2

Clock Source 时钟源:选择使用内部时钟

Channel1:输出 PWM 波

注意在这里设置完之后,引脚可能会自动跳变到默认引脚,这里是 PE9,还需要手动重新设置为我们蜂鸣器的 PA8。

2.2.3 框3

(1)预分频器

系统时钟 72M,AHB2 的时钟频率也是 72M ,故 PA8 的初始时钟也是 72M ,通过预分频器企图达到的目标评率为 1M 。

预分频器可以进行 0~65535 分频(因为零也算,所以后面要加一),计算预分频器具体分频数 X 的公式如下:

所以在软件配置的值为:72000000/1000000-1(目标频率是 1M )

(2)其他

Counter Mode 计数模式:向上

Counter Period 自动重装定时器:设置 PWM 波的频率

频率=预分频器频率/次数=1000000/1000=1000HZ

Internal Clock Division 内部时钟分频器:不需要

Repetition Counter 重复计数器:高级定时器功能,此处不需要

auto-reload preload 自动重新装载:必须使能,由于后续需要改变蜂鸣器的音色,就需要改变频率,该变频率就需要重装载。如果没有使能,后续即使改变 ARR 值夜不会重新装载进去。

2.2.4 框4

这里主要是设置占空比。

Mode 模式:PWM 模式 1

Pulse 脉冲(16 位值):500,这个值就是 CCR

可以这么理解,通过预分频器最后的时钟频率是1M,但是我还要自己更加灵活的控制PWM的频率,灵机一动想到了计数的方式。

所以才有了通过上面的设置让计数 1000 次(ARR),周期就变成了 1M×1000 =1ms 。

就这么利用一下这个计数,未免也太浪费资源了吧,既然它在向上计数,那就在中间设计一个比较值 CCR,利用 CCR 来调节占空比,小于 CCR 时 PWM 波输出高电平,大于 CCR 是 PWM 波输出低电平。)

Qutput compare preload 输出比较预加载:使能(可以改变占空比,而不是固定占空比)

极速模式:关闭

CH 极性:高(小于 CCR 的状态)

状态:重置

2.2.5达到的状态

每隔一毫秒中断一次,占空比为 50% 。

2.3编程

2.3.1文件结构

Buzzer.c:控制蜂鸣器的开启,并记录其状态。

Timer6.c:通过定时器 6 改变频率,发出不同的声音。

触摸按键:触发中断关闭或者打开蜂鸣器。

2.3.2文件内容

(1)运行函数

常规的、根据软件生成的初始化代码。在 while(1) 循环函数中:

main.c → System.c 

运行函数中没有内容,因为这一次是通过按键中断来改变声音。

(2)蜂鸣器结构体

Buzzer.h

Buzzer.c

#include "MyApplication.h"


static void Buzzer_ON(void); 
static void Buzzer_OFF(void); 

//定义结构体
Buzzer_t Buzzer = 
{
	OFF_Status,//默认蜂鸣器初始状态是关闭
	
	Buzzer_ON,
	Buzzer_OFF
    //两个函数赋给函数指针,函数如下
};


static void Buzzer_ON(void)//打开蜂鸣器
{
	Buzzer.Status = ON_Status;//更新一下状态为打开状态
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);//通过这个HAL库函数打开定时器(定时器1,通道1)
}


static void Buzzer_OFF(void)//关闭蜂鸣器,理论同上
{
	Buzzer.Status = OFF_Status;
	HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);
}
(3)Myinit.c

上电初始化只是打开蜂鸣器

(4)改变频率来改变音色

也是在 CallBack.c 文件中,共有三个参数:

  1. f:频率 → 通过改变 ARR(1000:1ms;2000:2ms;500:0.5ms)
  2. cnt:在某一频率是重复的时间长度
#include "MyApplication.h"

/*
	* @name   HAL_TIM_PeriodElapsedCallback
	* @brief  定时器中断回调函数
	* @param  *htim -> 处理定时器的结构体指针
	* @retval None      
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	static uint8_t Fre_Cnt = 0;
	
	if(htim->Instance == htim6.Instance)
	{
		//程序支持运行,指示灯间隔1s闪烁
		if(++Timer6.usMCU_Run_Timer >= TIMER0_1S)
		{
			Timer6.usMCU_Run_Timer = 0;
			
			LED.LED_Flip(LED1);
		}
		
		//控制PWM的频率长度与大小
		if(Fre_Cnt++ >= 2)
		{
			Fre_Cnt = 0;
			
			//定时器时钟 = 1MHz
			PWM频率 = 1/((1/1000000)*ARR) = 1000000/ARR  
			//ARR = 250, PWM频率为4KHz
			//ARR = 500, PWM频率为2KHz
			//ARR = 1000,PWM频率为1KHz
			//ARR = 2000,PWM频率为0.5KHZ
			TIM1->ARR -= 10;
			if(TIM1->ARR <= 500)
				TIM1->ARR = 2000;
			
			//设置占空比为50%
			TIM1->CCR1 = TIM1->ARR / 2;
		}
	}
}
(5)CallBack.c

只控制蜂鸣器打开或者关闭,按下按键就翻转蜂鸣器的状态。

#include "MyApplication.h"

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY1_Pin)
	{
		LED.LED_Flip(LED2);
		
		//控制蜂鸣器开关
		if(Buzzer.Status == ON_Status)
		{
			Buzzer.OFF();
		}
		else
		{
			Buzzer.ON();
		}
	}
}

2.3.3整体框架

2.3.4实验现象

PWM波驱动蜂鸣器

三、数码管

3.1程序功能

1、上电后,数码管间隔 50ms 计数;

2、触摸按键 1 调节数码管亮度,8 个等级。

3.2数码管驱动 IC 手册阅读

这节课老师就是带着将数据手册看了一遍,感觉听别人讲很枯燥,遂决定自己来看一遍,注意看不懂也没有关系,很多东西就是要和后续的编程结合起来才更容易弄懂。

3.2.1概述

TM1602 是一种 LED 驱动控制专用 IC,内部集成有 MCU 数字接口、数据锁存器、LED 驱动等电路。

3.2.2硬件电路

图3.1 硬件原理图

STB:片选输入;

CLK:时钟输入;

DIN:数据输入。

3.2.3指令说明

指令用来设置显示模式 LED 驱动器的状态。

在 STB 下降沿后由 DIN 输入的第一个字节作为指令,经过译码,取最高 B7、B6 两位比特位以区别不同的指令。

表3.1 指令说明
B7B6指令
00显示模式命令设置
01数据命令设置
10显示控制命令设置
11地址命令设置

如果在指令或数据传输时 STB 被置为高电平,串行通讯被初始化,并且正在传送的指令或数据无效(之前传送的指令或数据保持有效)。

(1)显示模式命令设置

该指令用来设置选择位和段的个数(4~6 位,8~10 段)。当该指令被执行时,显示被强制关闭。在显示模式不变时,显存内的数据不会被改变,显示控制命令控制显示开关。

图3.2 显示模式命令设置
(2)数据命令设置

该指令用来设置数据写和读,B1 和 B0 位不允许设置 01 或 11 。

图3.3 数据命令设置
(3)显示控制命令设置

该指令用来设置显示的开关以及显示亮度调节。共有 8 级辉度可供选择进行调节。 

图3.4 显示控制命令设置
(4)地址命令设置

该指令用来设置显示寄存器的地址。 最多有效地址为 12 位(00H-0BH)。上电时,地址默认设为00H。

图3.5 地址命令设置

3.2.4显示寄存器地址

该寄存器存储通过串行接口接收从外部器件传送到 TM1620 的数据,最多有效地址从 00H-0BH 共12 字节单元, 分别与芯片 SEG 和 GRID 管脚对应,具体分配如下图所示: 写 LED 显示数据的时候,按照显示地址从低位到高位,数据字节从低位到高位操作。

图3.6 显示寄存器地址

注意:芯片显示寄存器在上电瞬间其内部保存的值可能是随机不确定的,此时客户直接发送开屏命令, 将有可能出现显示乱码。所以我司建议客户对显示寄存器进行一次上电清零操作,即上电后向 12 位显存地址 (00H-0BH)中全部写入数据 0x00。

3.2.5串行数据传输格式

读取和接收 1 个 BIT 都在时钟的上升沿操作。

图3.7 串行数据传输格式

3.2.6应用时串行数据的传输

(1)地址增加模式

使用地址自动加 1 模式,设置地址实际上是设置传送的数据流存放的起始地址。起始地址命令字发送完毕,STB 不需要置高紧跟着传数据,最多  14BYTE ,数据传送完毕才将 STB 置高。

图3.8 地址增加模式
(2)固定地址模式

使用固定地址模式,设置地址其实际上是设置需要传送的 1BYTE 数据存放的地址。地址发送完毕 STB 不需要置高,紧跟着传 1BYTE 数据,数据传送完毕才将 STB 置高。然后重新设置第 2 个数据需要存放的地址,最多 12BYTE 数据传送完毕 STB 置高。

图3.9 固定地址模式

3.3初始化

Cubemx 初始化比较简单,只需要初始以下 3 个引脚。

不知道你们有没有这样的疑问:在硬件原理图中 TM1620_STB、TM1620_CLK、TM1620_DIN 这三个引脚明明是有 10K 的上拉电阻的,为什么在 Cubemx 中配置成无上下拉电阻?

答:在 Cubemx 中配置 GPIO 引脚的 Pull-up/Pull-down 设置时,No pull-up and no pull-down 选项表示 MCU 内部不会再加一个软件配置的上拉或下拉电阻。就是因为硬件上已经存在了 10kΩ 的上拉电阻,那么在软件上配置 MCU 内部再加上拉电阻就显得多余了。

3.4编程

3.4.1运行函数 System.c

从 1 到 999999 计数,计数过程是每 50ms 加一,显示在数码管上。

static void Run()
{
	static uint32_t Cnt = 0;
	
	//数码管显示计数值
	Display.Disp(Disp_NUM_1,Cnt%10,      Disp_DP_OFF); //个位
	Display.Disp(Disp_NUM_2,Cnt/10%10,   Disp_DP_OFF); //十位
	Display.Disp(Disp_NUM_3,Cnt/100%10,  Disp_DP_OFF); //百位
	Display.Disp(Disp_NUM_4,Cnt/1000%10, Disp_DP_OFF); //千位
	Display.Disp(Disp_NUM_5,Cnt/10000%10,Disp_DP_OFF); //万位
	Display.Disp(Disp_NUM_6,Cnt/100000,  Disp_DP_OFF); //十万位
	
	//更新计数值
	if(++Cnt > 999999)
		Cnt = 0;
	
	//延时50ms
	HAL_Delay(50);
}

3.4.2外部中断回调函数

通过触摸按键 1 中断的形式调节数码管的显示亮度,一共有 8 个显示亮度等级。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == KEY1_Pin)
	{
		LED.LED_Flip(LED2);
		
		switch(Display.Brightness)
		{
			case Brightness_level_1: Display.Brightness = Brightness_level_2; break;
			case Brightness_level_2: Display.Brightness = Brightness_level_3; break;
			case Brightness_level_3: Display.Brightness = Brightness_level_4; break;
			case Brightness_level_4: Display.Brightness = Brightness_level_5; break;
			case Brightness_level_5: Display.Brightness = Brightness_level_6; break;
			case Brightness_level_6: Display.Brightness = Brightness_level_7; break;
			case Brightness_level_7: Display.Brightness = Brightness_level_8; break;
			case Brightness_level_8: Display.Brightness = Brightness_level_1; break;
			default: Display.Brightness = Brightness_level_3;
		}
	}
}

3.4.3数码管 Display.h 头文件

(1)显示模式

往上翻,见图 3.2 显示模式命令设置,通过枚举的方式定义Disp_Mode_t:

4 位 10 段:00 0000 00:0x00;
5 位  9  段:00 0000 01:0x01;
6 位  8  段:00 0000 10:0x10。

在设置时输入最后面定义的数字即可。

(2)数据命令模式

往上翻,见图 3.3 数据命令设置,通过枚举的方式定义Write_Data_Addr_Mode_t,注意这里我们只使用普通模式、写数据到显示寄存器:

自动地址增加:01 00 0 0 00:0x40;
固定地址:       01 00 0 1 00:0x44。

(3)辉度等级

往上翻,见图 3.4 显示控制命令设置,通过枚举的方式定义 Brightness_level_t ,注意一共有 8 个辉度等级和一个关闭状态:

     显示关    :10 00 0 000 : 0x80;
显示开亮度1:10 00 1 000 : 0x88;
显示开亮度2:10 00 1 001 : 0x89;
显示开亮度3:10 00 1 010 : 0x8A;
显示开亮度4:10 00 1 011 : 0x8B;
显示开亮度5:10 00 1 100 : 0x8C;
显示开亮度6:10 00 1 101 : 0x8D;
显示开亮度7:10 00 1 111 : 0x8E;
显示开亮度8:10 00 1 111 : 0x8F。

(4)显示寄存器地址

往上翻,见图 3.5 地址命令设置,通过枚举的方式定义 Disp_SFR_Addr_t ,注意一共有 12 个寄存器:

(5)数码管

注意数码管的编号是从右至左,编号如下:

注意最右边的数字 DIG6 是 Disp_NUM_1。

(6)小数点

小数点的枚举定义是自定义的,跟数据手册没有直接的关系,直接定义了小数点的显示和不显示,后面写代码根据这个编写即可。

//数码管小数点状态
typedef enum
{
	Disp_DP_OFF  = 0x01,
	Disp_DP_ON   = 0x02,
} Disp_DP_Status_t;
(7)定义结构体类型
//定义结构体类型
typedef struct
{
	Brightness_level_t  Brightness;//就是上面辉度等级的枚举值
	
	void (*TM1620_Init)(void);                          //TM1620初始化,芯片建议在上电时将其缓存全部清除
	void (*Disp)(Disp_NUM_t,uint8_t,Disp_DP_Status_t);  //数码管显示,共三个参数(具体哪一个数码管,显示数据,小数点状态)
	//以上两个都是函数指针
} Display_t;

3.4.4数码管 Display.c 文件

/* Includes ------------------------------------------------------------------*/
#include "MyApplication.h"

/* Private define-------------------------------------------------------------*/
//TM1620串行通讯口
//通过宏定义进行3个管脚的置位和清零,方便移植
#define	SET_STB		HAL_GPIO_WritePin(TM1620_STB_GPIO_Port,TM1620_STB_Pin,GPIO_PIN_SET)
#define	CLR_STB		HAL_GPIO_WritePin(TM1620_STB_GPIO_Port,TM1620_STB_Pin,GPIO_PIN_RESET)

#define	SET_DIN		HAL_GPIO_WritePin(TM1620_DIN_GPIO_Port,TM1620_DIN_Pin,GPIO_PIN_SET)
#define	CLR_DIN		HAL_GPIO_WritePin(TM1620_DIN_GPIO_Port,TM1620_DIN_Pin,GPIO_PIN_RESET)

#define	SET_CLK		HAL_GPIO_WritePin(TM1620_CLK_GPIO_Port,TM1620_CLK_Pin,GPIO_PIN_SET)
#define	CLR_CLK		HAL_GPIO_WritePin(TM1620_CLK_GPIO_Port,TM1620_CLK_Pin,GPIO_PIN_RESET)


/* Private variables----------------------------------------------------------*/
//很好理解,就是0 - F在8段(abcdefgDP)数码管的译码
uint8_t Disp_Decode[16]    = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};  //数码管译码 0 - F

/* Private function prototypes------------------------------------------------*/
static void TM1620_Write_Byte(uint8_t);                 //TM1620写入字节

static void TM1620_Init(void);                          //TM1620初始化
static void Disp(Disp_NUM_t,uint8_t,Disp_DP_Status_t);  //数码管显示

/* Public variables-----------------------------------------------------------*/
Display_t Display = // 定义结构体变量,并做如下初始化
{
	Brightness_level_3,//初始化数码管亮度等级
	
	TM1620_Init,//赋给头文件里面的TM1602初始化函数指针
	Disp//赋给头文件里面的数码管显示函数指针
	//这样就可以通过结构体统一访问,以上函数在本页24、25中都定义成静态函数,对外不可见
};

/*
	* @name   TM1620_Write_Byte
	* @brief  TM1620写入字节
	* @param  dat -> 待写入数据
	* @retval None      
*/
static void TM1620_Write_Byte(uint8_t dat) //第一个函数:写字节//
{
	uint8_t i = 0;

	
	//参考数据手册的时序图(一定要去看图3.7)
	CLR_STB;//STB的下降沿就有了,表明//
	
	for(i=0;i<8;i++)//为8位数据弄的8次循环//
	{
		CLR_CLK;//在CLK上升沿读取数据,这里是CLK下降沿,故老称做是数据准备位//
		//准备数据位
		if((dat & BIT0) == BIT0) //BIT0为宏定义,值为0x01
			SET_DIN;//这里是将传送数据的最后一位与0x01=00000001,其实就是最后一位1,进行与操作//
		else        
			CLR_DIN;//与后还是1就置位,与后是0就清零,其实就是数据本身//
		dat = dat >> 1; //移位,准备下一个比特
		//时钟上升沿发送数据	
		SET_CLK;//时钟上升沿就是读取位数据之时//	
		__nop();//延时一下,等数据传输完,如此循环8次就传输完一个字节//
	}	
}

/*
	* @name   TM1620_Init
	* @brief  TM1620初始化
	* @param  None
	* @retval None      
*/
static void TM1620_Init() 
{
	uint8_t i = 0;
	
	//一定要去看图3.8,下面的过程就是根据图3.8写的//
	//设置显示模式
	TM1620_Write_Byte(Disp_Mode_GRID6_SEG8); //Command1:表示是6位8段,这段指令会变成8位数据传输,怎么传输就是去上面调第一个写字节的函数//
	SET_STB;//将SET置高,就是图3.8中STB的第一个凸起的置高,刚好下一个调用写字节函数时,会清零STB才有下降沿//  
	
  //地址自动增加
	TM1620_Write_Byte(Write_Data_Addr_Auto_Add); //Command2:表示将传输方式设置成自动增加,这段指令会变成8位数据传输,怎么传输就是去上面调第一个写字节的函数//
	SET_STB;//将SET置高,就是图3.8中STB的第二个凸起的置高,刚好下一个调用写字节函数时,会清零STB才有下降沿//  
	
	//清除显示寄存器
	TM1620_Write_Byte(Disp_SFR_Addr_00H); //设置首地址
	 //Command2:表示清除缓存,这段指令会变成8位数据传输,怎么传输就是去上面调第一个写字节的函数//
	//然后不用置置,直接往下运行,传输Data1~n(最多12位)//
	
	for(i=0;i<Disp_SFR_Addr_Num;i++)
		TM1620_Write_Byte(0x00); 
  SET_STB; //传输完之后再置高//
  //这也是为什么STB的清零写在写字节函数里面,不写在这,就是为了有时传输不止一位字节//

	//显示
	//这里是图3.9的Command5,,是显示控制命令,但是这里什么也不会显示,因为上面的代码将都清零了//
  TM1620_Write_Byte(Display.Brightness);
	SET_STB; 
}

/*
	* @name   Disp
	* @brief  数码管显示
  * @param  Disp_NUM:       数码管编号
            Dat:           数据 0 - F 
            Disp_DP_Status:小数点显示状态  //一共3个参数//
	* @retval None      
*/
static void Disp(Disp_NUM_t Disp_NUM,uint8_t Dat,Disp_DP_Status_t Disp_DP_Status) 
{
	//参数范围检查
	if(Dat > 0x0F)
	{
		System.Assert_Failed();//数码管每一位显示的数必须在0~F之间,如果不在,就进入参数错误的提示//
	}
	
	//设置显示模式
	TM1620_Write_Byte(Disp_Mode_GRID6_SEG8); //同上//
	SET_STB;
	
	//地址固定
	TM1620_Write_Byte(Write_Data_Addr_Fix); //为什么地址固定,是因为一次只控制一位数码管//
	SET_STB;
	
	//写地址
	TM1620_Write_Byte(Disp_NUM);//此处写地址是:上面三个参数中数码管的编号,该编号在头文件中已经有最右边到最左边数码管宏定义的地址了//
	//写数据
	if(Disp_DP_Status == Disp_DP_ON)//如果要小数点打开//
		TM1620_Write_Byte(Disp_Decode[Dat] + 0x80);//小数点是最高位,就是加上0x80//
	else
		TM1620_Write_Byte(Disp_Decode[Dat]);//如果不需要显示小数点,那就是数据本身了//
	SET_STB;
	
	//显示
  TM1620_Write_Byte(Display.Brightness);//Command5:显示控制命令,调节亮度值//
	SET_STB; 
}
//以上就完成了一位数码管的显示//
/*************************************
  End Of File
*************************************/

3.4.5程序架构


 注意:软件部分我都是简单学习,涉及到一些过程操作,我都写的比较简洁,如果需要请自行购买硬件家园相关课程。

本文推荐:硬件家园公众号

本文参考:资料汇总 - STM32物联网实战项目 (qq.com)

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值