前面的话
几经波折,来到这里的第二天晚上遇到了超级大雨,停电了。。。
现在一切恢复正常,把前几天的记录都补回来。
今日内容
实现了用数码管显示数字,并用按键来控制加减。
所用器件
1、51单片机(带四个按键)(STC15W4K56S4_LQFP64)
2、四位显示数码管
所需基础知识
1、如何代码实现按键加减
2、tm1650数码管驱动显示(LED驱动专用电路)
3、I2C通信协议
TM1650数码管驱动(一)
电路原理图
驱动介绍
(笔者是根据TM1650的开发手册来进行学习的,这里也是用该手册来进行介绍。)
想要看开发手册可以点击这里:戳这儿
通过时序格式以及引脚可以看出tm1650驱动使用的I2C通信协议。
I2C协议
I2C协议简介
IIC通讯协议(Inter----Integrted Circuit)是由Phiips飞利浦公司开发的,由于他引脚少,硬件实现简单,可拓展性强,不需要UASRT,CAN通讯协议的外部收发设备,现在被广泛使用在系统内多个集成电路IC(芯片)间的通讯。
半双工的通讯方式
IIC总线系统结构
他是一个支持多设备的总线。”总线”指多个设备共用的信号线,在一个IIC通讯总线中,可连接多个IIC通讯设备,支持多个通讯主机及多个通讯从机。
一个IIC总线只使用两条总线线路,一条双向串行数据线(SDA),一条串行时钟线(SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备直接的访问。
IIC总线物理层特点
总线通过上拉电阻接到电源。当IIC设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态,由上拉电阻把总线拉成高电平。
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定哪个设备占用总线。
具有三种传输模式:标准模式传输速率为100kbit/s,快速模式为400kbit/s,高速模式下可达3.4M/s,但目前大多IIC设备尚不支持高速模式。
IIC总线协议层
IIC协议层
IIC的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节
主机写数据到从机
S:数据由主机传输至从机
P:数据传输结束
SLAVE ADDRESS : 从机地址
起始信号产生后,所有从机就开始紧接下来广播的从机地址信号。IIC总线,每个设备的地址都是唯一的,当主机广播的地址与某个设备的地址相同时,这个设备就被选中了,没被选中的设备讲会忽略之后的数据信号。根据IIC协议,这个从机地址可以是7位或10位。
地址位之后,传输方向选择位,为0:表示数据传输方向是由主机传输至从机,即主机向从机写数据。为1:则相反。从机接收传输方向选择位后,主机或从机会返回一个应答(ACK)信号
或非应答(NACK)信号,
只有接收到应答信号后,主机才能继续发送或接收数据。
主机读数据到从机
读数据:
配置方向传输位为”读数据”方向。广播完地址后,接收到应答信号后,从机开始向主机返回数据(DATA),数据包大小也为8位,从机每发送完一个数,都会等待主机的应答信号(ACK),重复这个过程,可以返回N个数据,N没有限制大小。当主机希望停止接收数据时,就向从机返回一个非应答信号(NCAK),则从机自动停止数据传输。
通讯复合格式
复合格式,该传输过程有两次起始信号(S),在第一次传输过程中,主机通过SLAVE_ADDRESS寻找到从设备后,发送一段”数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址;第二次传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。
I2C通信的六点注意
① 空闲状态
②开始信号
③停止信号
④应答信号
⑤数据的有效性
⑥数据传输
①空闲信号
IIC总线的SDA和SCL两条信号线同时处于高电平时,规定位总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
②开始信号 、③停止信号
起始信号:当SCL为高电平期间,SDA有高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高电平期间,SDA由低到高的跳变;停止信号也是一种高电平跳变时序信号,而不是一个电平信号
起始信号和停止信号一般由主机产生
④应答信号
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位)表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
对于反馈有效应答位ACK的要求是,接收器在第九个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。
如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器数据发送,并释放SDA线,以便主控接收器发送一个停止信号P。
⑤数据的有效性
IIC总线进行数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。SDA数据线在SCL的每个时钟周期传输一位数据。
即:数据在SCL的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定
⑥数据传输
在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
TM1650数码管驱动(二)
TM1650 时序图
操作指令集[(系统/按键)/ 亮度 ]
四位数码管地址与八位led灯的显示
操作流程图
代码
必要的知识内容已经讲完,下面开始上代码!!
头文件
#include<STC15F2K60S2.h>
#include<intrins.h>
对数据类型进行定义
#define uchar unsigned char //对数据类型进行声明定义
#define uint unsigned int
定义数码管驱动引脚 以及按键
sbit CLK = P7^0; //时钟线
sbit DIO = P7^1; //数据线
//TM1650管脚的定义声明
sbit k1=P6^0; //定义P60口是k1
sbit k2=P6^1; //定义P61口是k2
sbit k3=P6^2; //定义P62口是k3
sbit k4=P6^3; //定义P62口是k3
sbit led=P5^5; //定义P55口是led
//四个按键 以及 led显示灯的定义声明
宏定义时钟线、数据线的电平操作
#define CLK_H CLK = 1;
#define CLK_L CLK = 0;
#define DIO_H DIO = 1;
#define DIO_L DIO = 0;
函数声明
void TM1650_Set(uchar add,uchar dat);//函数声明
uchar Scan_Key(void);
void Delay_us(uint i);
uchar z=0;
uchar i,j;
uchar flag=0;
uchar code CODE[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0xff,0x6f};
延时函数
/*********************************************************
* 函 数 名 : Delay_us
* 函数功能 : 延时函数,i=1时,大约延时10us
*********************************************************/
void Delay_us(uint i)
{
for(;i>0;i--)
{
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
}
数码管驱动 I2C时序 开始函数
/*********************************************************
* 函 数 名 : I2CStart
* 函数功能 : I2C起始函数
*********************************************************/
void I2CStart(void)
{
CLK_H;
DIO_H;
Delay_us(5);
DIO_L;
}
数码管驱动 I2C时序 应答函数
/*****************************************************
* 函 数 名 : I2CCask
* 函数功能 : I2C确认函数
*****************************************************/
void I2Cask(void)
{
uchar timeout = 1;
CLK_H;
Delay_us(5);
CLK_L;
while((DIO)&&(timeout<=100))
{
timeout++;
}
Delay_us(5);
CLK_L;
}
数码管驱动 I2C时序 停止函数
/*************************************************************
* 函 数 名 : I2CStop
* 函数功能 : I2C停止函数
*************************************************************/
void I2CStop(void)
{
CLK_H;
DIO_L;
Delay_us(5);
DIO_H;
}
数码管驱动 I2C时序 写数据函数
/*************************************************
* 函 数 名 : I2CWrByte
* 函数功能 : 写字节函数
* 输 入 : 一个字节
* 输 出 : 无
************************************************/
void I2CWrByte(uchar oneByte)
{
uchar i;
CLK_L;
Delay_us(1);
for(i=0;i<8;i++)
{
oneByte = oneByte<<1;
DIO = CY;
CLK_L;
Delay_us(5);
CLK_H;
Delay_us(5);
CLK_L;
}
}
数码管驱动 向指定地址传入数据
/*******************************************************
* 函 数 名 : TM1650_Set
* 函数功能 : 向指定地址写入数据函数
* 输 入 : add 写入的地址;dat 要写入的数据
* 输 出 : 无
*******************************************************/
void TM1650_Set(uchar add,uchar dat)
{
I2CStart();
I2CWrByte(add);
I2Cask();
I2CWrByte(dat);
I2Cask();
I2CStop();
}
按键函数
/*******************************************************
* 函 数 名 : keypros
* 函数功能 : 按键处理函数,判断按键K1是否按下
********************************************************/
void keypros()
{
if(k3==0)
{
Delay_us(100); //消除抖动 一般大约10ms
if(k3==0)
{
flag=1; //led状态取反
// led=~led;
}
while(!k3); //检测按键是否松开
}
if(k4==0)
{
Delay_us(1000); //消除抖动 一般大约10ms
if(k4==0)
{
flag=0; //led状态取反
// led=~led;
}
while(!k4); //检测按键是否松开
}
if(flag==1)
{
if(k1==0) //检测按键K1是否按下
{
Delay_us(1000); //消除抖动 一般大约10ms
if(k1==0&&z<99) //再次判断按键是否按下
{
// led=~led; //led状态取反
z++;
}
TM1650_Set(0x68,CODE[z/10]);
TM1650_Set(0x6a,CODE[z%10]);
while(!k1); //检测按键是否松开
}
if(k2==0) //检测按键K1是否按下
{
Delay_us(1000); //消除抖动 一般大约10ms
if(k2==0&&z>0) //再次判断按键是否按下
{
// led=~led; //led状态取反
z--;
}
TM1650_Set(0x68,CODE[z/10]);
TM1650_Set(0x6a,CODE[z%10]);
while(!k2); //检测按键是否松开
}
}
if(flag==0)
{
TM1650_Set(0x48,0x51);
TM1650_Set(0x68,CODE[0]);
TM1650_Set(0x6a,CODE[0]);
}
}
main函数
/*******************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
********************************************************/
void main()
{
flag=0;
while(1)
{
keypros();
}
}
写在最后
”行胜于言“