【STM32标准库】【自制库】8位8段数码管(74HC595)【软件部分】


文章基于适用于STM32F4系列,作者使用STM32F401CCU6开发板。
本文章基于此系列和开发板展开讨论。

硬件部分

模块的介绍和硬件原理在这里
【基础知识】【模块介绍】8位8段数码管(74HC595)【硬件部分】

软件设计

需求分析

  • 8位“同时”显示
  • 数码管可以显示0-F的数值和横线作为分隔符
  • 可以设置小数点位置
  • 能动态修改要显示的值
  • 显示过程中可以进行其他运算

编程思路

总体上使用硬件部分提到过的扫描,间隔一段时间分别点亮8位数码管,使用定时器中断产生一定的时间来减少CPU的占用。

  1. 字码数据编写
  2. 初始化(GPIO,NVIC,定时器)
  3. 数据发送程序
  4. 定时扫描程序

字码数据和宏定义

这是定义在头文件中,为了便于使用
这里使用了固件库的头文件和上次的延迟函数文件

TUBE.H

#ifndef _TUBE_H
#define _TUBE_H

#include "stm32f4xx.h"                  				// 注意更改这个文件的#define STM32F401xx为自己的芯片型号
#include "Delay.h"										//自制的延迟文件

//更改端口时需要更改(是关于GPIO配置所需要的东西)
#define Tube_SCLK			GPIO_Pin_0
#define Tube_RCLK			GPIO_Pin_1
#define Tube_DIO			GPIO_Pin_2
#define Tube_RCC			RCC_AHB1ENR_GPIOAEN
#define Tube_GPIOx			GPIOA

//更改定时器时需要更改(关于定时器配置所需要的东西)
#define	Tube_Tim_RCC_Cmd	{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);}	//这里因为不确定所使用的定时器,因此直接将打开时钟函数编写成宏定义
#define	Tube_Tim			TIM3
#define	Tube_Tim_IRQChannel	TIM3_IRQn			
#define Tube_Priority_1		3							//主中断优先级设置
#define Tube_Priority_2		3							//副中断优先级

//数码管的值 按照共阳接法的字码值
typedef enum
{
	Tube_0		=	0xC0,
	Tube_1		=	0xF9,
	Tube_2		=	0xA4,
	Tube_3		=	0xB0,
	Tube_4		=	0x99,
	Tube_5		=	0x92,
	Tube_6		=	0x82,
	Tube_7		=	0xF8,
	Tube_8		=	0x80,
	Tube_9		=	0x90,
	Tube_A		=	0x88,
	Tube_b		=	0x83,
	Tube_C		=	0xC6,
	Tube_d		=	0xA1,
	Tube_E		=	0x86,
	Tube_F		=	0x8E,
	Tube_Line	=	0xbf,						//显示-
	Tube_Bleak	=	0xff,						//全不显示
	Tube_Spot	=	0x7F,						//显示小数点
	Tube_White	=	0x00						//全都显示
}Tube_Val_Typedef;

//这里的几个变(常)量定义在 TUBE.C 文件中,因此使用extern关键字
extern Tube_Val_Typedef Tube_Val[8];			//这个是存的
extern const Tube_Val_Typedef Tube_Lest[16];	//这个是0-F的字符,使用时直接按数组的方式调用
extern uint8_t Tube_Spot_P;						//小数点位置,从左向右数的位数

//函数定义,这下面的就是所使用的全部函数了
void Time_Init(void);
void Tube_Out_Send(uint8_t X);
void Tube_Show(void);
void Tube_Out(uint8_t Digit,Tube_Val_Typedef Data,FunctionalState Spot);
void Time_Init(void);
void Tube_Clear(void);
void Tube_Init(void);
void Tube_Send_8Bit(Tube_Val_Typedef *Data,uint8_t Spot_P);
void Tube_Send_Scan(Tube_Val_Typedef *Data,uint8_t Spot_P);


//更改定时器时需要更改,如出现重复定义请删除另一个
//重定义可能是你已经使用过这个定时器的中断服务了,也可能是固件库预先写在一个文件中了,注意辨别即可
void TIM3_IRQHandler(void);

#endif

TUBE.c

//8个数码管的值 从左到右
Tube_Val_Typedef Tube_Val[8]={Tube_White,Tube_White,Tube_White,Tube_White,Tube_White,Tube_White,Tube_White,Tube_White};

//数值列表分别是0-f
const Tube_Val_Typedef Tube_Lest[16]={Tube_0,Tube_1,Tube_2,Tube_3,Tube_4,Tube_5,Tube_6,Tube_7,Tube_8,Tube_9,Tube_A,Tube_b,Tube_C,Tube_d,Tube_E,Tube_F};

//小数点的位置 从左到右数
uint8_t Tube_Spot_P=0xff;

定义时使用的初值

初始化

GPIONVIC定时器中断的配置之前介绍过了,点击查看详情。

定时器中断,NVIC初始化配置

//定时器和中断初始化
void Time_Init(void)
{
	
	TIM_TimeBaseInitTypeDef TIM_Init_TypeDef;
	NVIC_InitTypeDef NVIC_Init_TypeDef;
	Tube_Tim_RCC_Cmd											//打开时钟,这是个宏定义,在头文件中
	TIM_Init_TypeDef.TIM_ClockDivision=TIM_CKD_DIV1;			//滤波器不分频
	TIM_Init_TypeDef.TIM_CounterMode=TIM_CounterMode_Up;		//向上计数模式

	TIM_Init_TypeDef.TIM_Period=840 - 1;						//预分频
	TIM_Init_TypeDef.TIM_Prescaler=250 - 1;						//周期
	/*2.5ms*/
	
	TIM_Init_TypeDef.TIM_RepetitionCounter=0;					//通用定时器设0就行

	TIM_TimeBaseInit(Tube_Tim,&TIM_Init_TypeDef);				//初始化
	TIM_ITConfig(Tube_Tim,TIM_IT_Update,ENABLE);				//开定时器溢出中断
	
	NVIC_Init_TypeDef.NVIC_IRQChannel=Tube_Tim_IRQChannel;		//宏定义,看头文件
	NVIC_Init_TypeDef.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init_TypeDef.NVIC_IRQChannelPreemptionPriority=Tube_Priority_1;
	NVIC_Init_TypeDef.NVIC_IRQChannelSubPriority=Tube_Priority_2;
	NVIC_Init(&NVIC_Init_TypeDef);								
	TIM_Cmd(Tube_Tim,ENABLE);									//别忘打开定时器
}

GPIO初始化和整个工程的初始化,使用时直接调用此函数即可

//初始化
void Tube_Init(void)
{
	//GPIO初始化
	GPIO_InitTypeDef GPIO_InitTypeDef;
	RCC_AHB1PeriphClockCmd(Tube_RCC,ENABLE);
	GPIO_InitTypeDef.GPIO_Mode=GPIO_Mode_OUT;					//输出模式
	GPIO_InitTypeDef.GPIO_OType=GPIO_OType_OD;					//开漏输出,安全一些
	GPIO_InitTypeDef.GPIO_Pin=Tube_SCLK|Tube_RCLK|Tube_DIO;
	GPIO_InitTypeDef.GPIO_PuPd=GPIO_PuPd_UP;					//上拉
	GPIO_InitTypeDef.GPIO_Speed=GPIO_Fast_Speed;
	GPIO_Init(Tube_GPIOx,&GPIO_InitTypeDef);
	GPIO_ResetBits(Tube_GPIOx,Tube_SCLK|Tube_RCLK|Tube_DIO);	//初始设为0
	
	//NVIC组设定
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//在调用NVIC初始化前设置
	
	//定时器,NVIC初始化
	Time_Init();
	
	//全部点亮0.5s后关闭  默认值是全部点亮,500ms后设为黑就行了
	Delay_ms(500);
	Tube_Clear();					//就是改Tube_Val数组的数值
}

数据发送

在硬件部分中说过,模块内有2个595芯片分别存入数据和片选
根据硬件部分中的介绍可以知道

先发送字码,后发送片选码,按照从高到低的顺序发送,在SCLK的上升沿送入数据
片选码的顺序的从高到低对应的是模块上的从左到右
RCLK的上升沿将显示

发送数据

//发送数据
void Tube_Out_Send(uint8_t X)
{
	uint8_t i;
	//从高到低发送数据
	for(i=8;i>=1;i--)
	{	
		if (X&0x80)
		{
			GPIO_WriteBit(Tube_GPIOx,Tube_DIO,Bit_SET);
		}
		else
		{
			GPIO_WriteBit(Tube_GPIOx,Tube_DIO,Bit_RESET);
		}
		X<<=1;
		//必须加延迟
		Delay_us(2);
		//在SCLK中产生上升沿 表示一位数据送入
		GPIO_WriteBit(Tube_GPIOx,Tube_SCLK,Bit_RESET);
		Delay_us(2);
		GPIO_WriteBit(Tube_GPIOx,Tube_SCLK,Bit_SET);
		Delay_us(2);
	}
}

注意:需要加上延迟,STM32的GPIO翻转速度超过了595的极限,推荐2us

显示

//显示
void Tube_Show(void)
{
	//在RCLK产生上升沿 锁存显示
	GPIO_WriteBit(Tube_GPIOx,Tube_RCLK,Bit_RESET);
	GPIO_WriteBit(Tube_GPIOx,Tube_RCLK,Bit_SET);
}

发送一位8段数码管的数据

//发送一位数码管的数据 Digit:从左向右数的数据位数 Data:数据 Spot:是否显示小数点
void Tube_Out(uint8_t Digit,Tube_Val_Typedef Data,FunctionalState Spot)
{
	//先发送要显示的值
	if(Spot==ENABLE)
	{
		Tube_Out_Send(0x7F&Data);		//设置小数点
	}
	else
	{
		Tube_Out_Send(Data);
	}

	//后发送显示的位置
	Tube_Out_Send(0x80>>Digit);
	Tube_Show();
}

扫描

发送8位的数据(一种扫描方式,实验时用的,每个数码管的点亮时间很短,导致亮度不高,如果需要较低亮度,可以使用这个配合定时器时间实现)

//Data:数据数组0位显示在最左边 Spot_P:为从左向右数点的位置
void Tube_Send_8Bit(Tube_Val_Typedef *Data,uint8_t Spot_P)
{
	for(int i=0;i<8;i++)
	{
		if(i==Spot_P)
		{
			Tube_Out(i,Data[i],ENABLE);
		}
		else
		{
			Tube_Out(i,Data[i],DISABLE);
		}
		
	}
}

发送8位数据,需要按照配合定时器,需要多次调用此函数
和上面那个函数不同的是,本函数的每位点亮周期为定时器中断的产生 周期,因此亮度高,能基本上接近一位点亮时亮度,但频率较低时会出现肉眼可见的闪烁

//定时器扫描用
void Tube_Send_Scan(Tube_Val_Typedef *Data,uint8_t Spot_P)
{
	static char i=0;
	if(i==Spot_P)
	{
		Tube_Out(i,Data[i],ENABLE);
	}
	else
	{
		Tube_Out(i,Data[i],DISABLE);
	}
	i++;
	if (i>7)
	{
		i=0;
	}
	Tube_Show();
}

中断服务函数(换定时器时要改这个名称)

//中断服务函数 扫描8个数码管
void TIM3_IRQHandler(void)
{
	if( TIM_GetITStatus(Tube_Tim,TIM_IT_Update)  != RESET)
	{
		Tube_Send_Scan(Tube_Val,Tube_Spot_P);
		// Tube_Send_8Bit(Tube_Val,Tube_Spot_P);
		TIM_ClearITPendingBit(Tube_Tim,TIM_IT_Update);
	}
}

两种扫描方式的示例都放进去了,了可以尝试下,根据需要设置

成品

已经打包为头文件,所有文件均可免费下载
CSDN

链接:百度网盘
提取码:82ig
演示图片

模式2(低亮)
在这里插入图片描述

模式2(高亮)
在这里插入图片描述

注意:这个模块的供电是3.3到5v均可,一定要注意电源和信号的匹配,别用5v供电给模块,3.3的高低电平,很可能无法驱动

stm32版八位串行595数码管 #include "sys.h" //点击魔术棒加入对应的文件夹地址,FWLIB里加入相应的头文件 #include "delay.h" #include "usart.h" #include "led.h" #include "key.h" //////////////////////////////////////////////////////////////////////////// unsigned char fseg[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; unsigned char segbit[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; unsigned char disbuf[8]={0,0,0,0,0,0,0,0}; #define uchar unsigned char #define DIO LED0//串行数据输入 #define RCLK LED1 //时钟脉冲信号——上升沿有效 #define SCLK LED2//打入信号————上升沿有效 void LED4_Display (void); // LED显示 void LED_OUT(uchar X); // LED单字节串行移位函数 void data_OUT(int data); unsigned char LED_0F[]; // LED字模表 //----------------------------------------------------------------------------- // 全局变量 uchar LED[8]; //用于LED的8位显示缓存 /////////////////////////////////////////////////////////////////////////////////// int main(void) { u8 t=0; delay_init(); //延时函数初始化 NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 uart_init(9600); //串口初始化为9600 LED_Init(); KEY_Init(); ////////////////////////////////////////////////////////// // LED[0]=0; // LED[1]=2; // LED[2]=3; // LED[3]=4; // LED[4]=5; // LED[5]=6; // LED[6]=7; // LED[7]=8; ///////////////////////////////////////////////////////////////// while(1) { LED4_Display ();//串口数码管 data_OUT(123456); // printf("love"); } } ///////////////////////////////////////////////////////////////// void LED4_Display (void) { unsigned char *led_table; // 查表指针 uchar i; //显示第1位 led_table = LED_0F + LED[0]; i = *led_table; LED_OUT(i); LED_OUT(0x01); RCLK = 0; RCLK = 1; //显示第2位 led_table = LED_0F + LED[1]; i = *led_table; LED_OUT(i); LED_OUT(0x02); RCLK = 0; RCLK = 1; //显示第3位 led_table = LED_0F + LED[2]; i = *led_table; LED_OUT(i); LED_OUT(0x04); RCLK = 0; RCLK = 1; //显示第4位 led_table = LED_0F + LED[3]; i = *led_table; LED_OUT(i); LED_OUT(0x08); RCLK = 0; RCLK = 1; //显示第5位 led_table = LED_0F + LED[4]; i = *led_table; LED_OUT(i); LED_OUT(0x10); RCLK = 0; RCLK = 1; //显示第6位 led_table = LED_0F + LED[5]; i = *led_table; LED_OUT(i); LED_OUT(0x20); RCLK = 0; RCLK = 1; //显示第7位 led_table = LED_0F + LED[6]; i = *led_table; LED_OUT(i); LED_OUT(0x40); RCLK = 0; RCLK = 1; //显示第8位 led_table = LED_0F + LED[7]; i = *led_table; LED_OUT(i); LED_OUT(0x80); RCLK = 0; RCLK = 1; } void LED_OUT(uchar X) { uchar i; for(i=8;i>=1;i--) { if (X&0x80) DIO=1; else DIO=0; X<<=1; SCLK = 0; SCLK = 1; } } void data_OUT(int data) { LED[7] = 0; LED[6] = 0; LED[5] = data % 1000000 / 100000; LED[4] = data % 100000 / 10000; // LED[3] = data % 10000 / 1000; LED[2] = data % 1000 / 100; LED[1] = data % 100 / 10; LED[0] = data % 10; } unsigned char LED_0F[] = {// 0 1 2 3 4 5 6 7 8 9 A b C d E F - 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x8C,0xBF,0xC6,0xA1,0x86,0xFF,0xbf }; ///////////////////////////////////////////////////////////////////////////////////////////////
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值