大二课设,实现单片机对单片机的多机串口通信
用到的资源有:STC89C52,LCD1602,AT24C02,面包板,公对母杜邦线,母对母杜邦线,矩阵键盘(s0~s9),独立按键(k0~k3),定时器T1,定时器中断,外部中断,串口中断
——————————基本流程———————————
将LCD1602的字符库各个字符的ascll码存储进AT24C02
LCD1602初始化
矩阵键盘初始化:s0~s9,按下返回各自对应的值,s1返回值1,s2返回值2,以此类推 设置定时器T1,工作方式1,波特率为9600 主机中断0:发送数据 主机中断1:设置发送的地址 从机中断0:发送数据 主机和从机串口中断:接受数据帧 ————————供能描述——————————
实现的功能大概可以简述为: 先将lcd1602的字符库存储进AT24C02的地0~n里,读取时通过矩阵键盘返回的值去读取对应地址内存储的字符库数据,然后就可以在LCD中显示相应的字符
字符库和地址对应表: 主机:k3按下对应进入中断0设置要发送的地址,地址由矩阵键盘返回值确定,k2按下进对应的中断1,矩阵键盘输入对应字符AT24C02中的地址,在按下k2可继续输入下一个,按下k1结束输入,将输入的字符库地址发送给从机
从机:从机和主机类似,只不过去掉了k3按下发送地址的步骤,主机只有一个,从机向主机发送数据时不需要地址帧 通信没有设置校验位:RB8和TB8用于数据帧和地址帧的判断,当RB8=1时说明接受的为地址帧,当RB8=0时说明接受的是数据帧,当TB8=1时说明发送的是地址帧,当TB8=0时说明发送的是数据帧 例如:
主机发送:(TB8=1➕地址➕TB8=0➕数据)为一发送数据帧
主机接受:(RB8=0➕数据 )为一接受数据帧
从机发送:(TB8=0➕数据)为一发送数据帧
从机接受: (RB8=1➕地址➕RB8=据)为一接受数据帧
————————流程图和链接图————————
储存模块
———————————IIC函数———————————
#include <REGX52.H>
sbit I2C_SCL=P2^1;//定义scl,sda引脚
sbit I2C_SDA=P2^0;
//开始帧
void I2C_Start(void)
{
I2C_SDA=1;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
}
//停止帧
void I2C_Stop(void)
{
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
for(i=0;i<8;i++)
{
I2C_SDA=Byte&(0x80>>i);
I2C_SCL=1;
I2C_SCL=0;
}
}
/**
* @brief I2C接收一个字节
* @param 无
* @retval 接收到的一个字节数据
*/
unsigned char I2C_ReceiveByte(void)
{
unsigned char i,Byte=0x00;
I2C_SDA=1;
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA){Byte|=(0x80>>i);}
I2C_SCL=0;
}
return Byte;
}
/**
* @brief I2C发送应答
* @param AckBit 应答位,0为应答,1为非应答
* @retval 无
*/
void I2C_SendAck(unsigned char AckBit)
{
I2C_SDA=AckBit;
I2C_SCL=1;
I2C_SCL=0;
}
/**
* @brief I2C接收应答位
* @param 无
* @retval 接收到的应答位,0为应答,1为非应答
*/
unsigned char I2C_ReceiveAck(void)
{
unsigned char AckBit;
I2C_SDA=1;
I2C_SCL=1;
AckBit=I2C_SDA;
I2C_SCL=0;
return AckBit;
}
————————AT24C02模块——————————
包含向AT24C02写入数据,读出数据,以及本设计需要的,向其写入LCD1602字符库
#include <REGX52.H>
#include "I2C.h"
#include "DELAY.H"
#define AT24C02_ADDRESS 0xA0
/**
* @brief AT24C02写入一个字节
* @param WordAddress 要写入字节的地址
* @param Data 要写入的数据
* @retval 无
*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
}
/**
* @brief AT24C02读取一个字节
* @param WordAddress 要读出字节的地址
* @retval 读出的数据
*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS|0x01);
I2C_ReceiveAck();
Data=I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}
//向AT24C02写入数据,WordAddress地址,Data数据
void AT24C02_WRITE(unsigned char WordAddress,Data)
{
AT24C02_WriteByte(WordAddress-1,Data%256);
Delay(5);
AT24C02_WriteByte(WordAddress,Data/256);
Delay(5);
}
//AT24C02读取数据,WordAddress地址
unsigned char AT24C02_READ(unsigned char WordAddress)
{
unsigned char Data;
Data=AT24C02_ReadByte(WordAddress-1);
Data|=AT24C02_ReadByte(WordAddress)<<8;
}
//向AT24C02写入LCD1602字符库
void ALPHABET_WRIET(void)
{
unsigned char ALPHABET_NUMBER=0;
AT24C02_WRITE(0,0xfe);
for(ALPHABET_NUMBER=0;ALPHABET_NUMBER<224;ALPHABET_NUMBER++)
{
AT24C02_WRITE(ALPHABET_NUMBER+2,0x20+ALPHABET_NUMBER);
}
}
————————LCD1602模块——————————
#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
* @brief LCD1602延时函数,12MHz调用可延时1ms
* @param 无
* @retval 无
*/
void LCD_Delay()
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
/**
* @brief LCD1602写命令
* @param Command 要写入的命令
* @retval 无
*/
void LCD_WriteCommand(unsigned char Command)
{
LCD_RS=0;
LCD_RW=0;
LCD_DataPort=Command;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602写数据
* @param Data 要写入的数据
* @retval 无
*/
void LCD_WriteData(unsigned char Data)
{
LCD_RS=1;
LCD_RW=0;
LCD_DataPort=Data;
LCD_EN=1;
LCD_Delay();
LCD_EN=0;
LCD_Delay();
}
/**
* @brief LCD1602设置光标位置
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @retval 无
*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
if(Line==1)
{
LCD_WriteCommand(0x80|(Column-1));
}
else if(Line==2)
{
LCD_WriteCommand(0x80|(Column-1+0x40));
}
}
/**
* @brief LCD1602初始化函数
* @param 无
* @retval 无
*/
void LCD_Init()
{
LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
* @brief 在LCD1602指定位置上显示一个字符
* @param Line 行位置,范围:1~2
* @param Column 列位置,范围:1~16
* @param Char 要显示的字符
* @retval 无
*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Char);
}
/**
* @brief 在LCD1602指定位置开始显示所给字符串
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串
* @retval 无
*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=0;String[i]!='\0';i++)
{
LCD_WriteData(String[i]);
}
}
/**
* @brief 返回值=X的Y次方
*/
int LCD_Pow(int X,int Y)
{
unsigned char i;
int Result=1;
for(i=0;i<Y;i++)
{
Result*=X;
}
return Result;
}
/**
* @brief 在LCD1602指定位置开始显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~65535
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-32768~32767
* @param Length 要显示数字的长度,范围:1~5
* @retval 无
*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
unsigned char i;
unsigned int Number1;
LCD_SetCursor(Line,Column);
if(Number>=0)
{
LCD_WriteData('+');
Number1=Number;
}
else
{
LCD_WriteData('-');
Number1=-Number;
}
for(i=Length;i>0;i--)
{
LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
}
}
/**
* @brief 在LCD1602指定位置开始以十六进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFF
* @param Length 要显示数字的长度,范围:1~4
* @retval 无
*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i,SingleNumber;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10)
{
LCD_WriteData(SingleNumber+'0');
}
else
{
LCD_WriteData(SingleNumber-10+'A');
}
}
}
/**
* @brief 在LCD1602指定位置开始以二进制显示所给数字
* @param Line 起始行位置,范围:1~2
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
unsigned char i;
LCD_SetCursor(Line,Column);
for(i=Length;i>0;i--)
{
LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
}
}
void myLCDshow(unsigned char Line,unsigned char Column,unsigned char Data)
{
LCD_SetCursor(Line,Column);
LCD_WriteData(Data);
}
————————矩阵键盘模块——————————
#include <REGX52.H>
#include "Delay.h"
/**
* @brief 矩阵键盘读取按键键码
* @param 无
* @retval KeyNumber 按下按键的键码值
如果按键按下不放,程序会停留在此函数,松手的一瞬间,返回按键键码,没有按键按下时,返回10
*/
unsigned char MatrixKey()
{
unsigned char KeyNumber=10;
do
{
Delay(20);
P1=0xFF;
P1_3=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=0;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=4;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=8;}
P1=0xFF;
P1_2=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
P1=0xFF;
P1_1=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
P1=0xFF;
P1_0=0;
if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
}while(KeyNumber==10);
return KeyNumber;
}
—————————定时器和中断模块———————
#include <REGX52.H>
void Tim2()
{
TMOD=0x20;
TH1=0xfd;
TL1=0xfd;
TR1=1;
}
void Scon()
{
EA=1;
ES=1;
SCON=0xf0;
PCON=0x00;
}
void Exit0()
{
EA=1;
EX0=1;
IT0=1;
}
void Exit1()
{
EA=1;
EX1=1;
IT1=1;
}
————————延时函数模块——————————
#include <REGX52.H>
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
——————————主函数模块—————————
包含发送地址中断0,发送数据中断1,接受中断4
#include <REGX52.H>
#include "LCD1602.H"
#include "SCON_WR.H"
#include "MATRIXKEY.H"
#include "AT24C02.H"
#include "INTERRUPT.H"
#include "DELAY.H"
#define MyAddress 2
static unsigned char Data_s[16]=0,number=0,i=0;
void main()
{
Tim2();
Scon();
Exit0();
if(AT24C02_READ(0)!=255){ALPHABET_WRIET();}
LCD_Init();
while(1)
{
P1_7=0;
if(P1_3==0){LCD_WriteCommand(0x01);number=0;}
}
}
void Exit_0() interrupt 0//发送数据
{
unsigned char Data[50]=0,i=0,j=0,k=0,m=0;
int Data_a=0;
LCD_Init();
LCD_ShowString(1,1,"INPUT DATA:");
while(1)
{
if(P3_0==0){break;}
if(P3_2==0){
LCD_ShowNum(1,14,Data_a,3);
Data_a=MatrixKey()*100;
LCD_ShowNum(1,14,Data_a,3);
Data_a+=MatrixKey()*10;
LCD_ShowNum(1,14,Data_a,3);
Data_a+=MatrixKey();
LCD_ShowNum(1,14,Data_a,3);
if(Data_a>244){LCD_ShowString(2,1,"INPUT EOR!");Delay(500);LCD_Init();}
else {myLCDshow(2,i+1,AT24C02_READ(Data_a));Data[i]=Data_a;i++;}
}
}
for(j=0;j<i;j++) //循环输出Data[j]
{
SBUF=Data[j];
while(!TI);TI=0;
Delay(10);
}
LCD_ShowString(2,1,"SEND DATA OK");
Delay(500);
LCD_Init();
IE0=0;
}
void s() interrupt 4
{
unsigned char m,Data,k;
if(RB8==1&&MyAddress==SBUF)
{
SM2=0;
}
if(RB8==0)
{
SM2=1;
Data=SBUF;
if(number==16){LCD_WriteCommand(0x01);number=0;}
number++;
myLCDshow(2,number,AT24C02_READ(SBUF));
}
RI=0;
}