简介
按照相应的编码格式,对串口数据进行编码,发送至单片机,单片机对数据进行解析,写入或者读取eeprom,再返回数据至单片机串口。
串口
读取数据0x03 且只需要传入3个16进制数即可,写入数据0x06,具体不超过255个数据,要想多写入数据,可以将数据长度的值增加到两个16进制数,程序也需要相应改变,对两个16进制数运算,整合成一个16位的数据。
地址 | 数据标志 | 数据长度 | 值 |
---|---|---|---|
0x01 | 0x03 | 0x05 | 0x01.0x02… |
代表单片机的地址,用来区分单片机 | 03是读取数据,06是写入数据 | 代表读取和写入数据的长度 | 写入数据时填写要写入数据的值,读取数据不需要进行编码 |
eeprom
- 51单片机的eeprom扇区一共有八个,这里主要对第一个扇区进行操作。
- 在对扇区进行写操作时,需要先将扇区进行擦除,才可以将数据写进去。
程序
eeprom
这里对于eeprom的操作是使用的这里的库函数文件,感谢!
https://blog.csdn.net/hchanghao/article/details/6847976?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522161104904616780262544248%252522%25252C%252522scm%252522%25253A%25252220140713.130102334.pc%25255Fall.%252522%25257D&request_id=161104904616780262544248&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-1-6847976.pc_search_result_cache&utm_term=%E9%BB%84%E9%95%BF%E6%B5%A9
.h文件
/*
When Who Remarks
----------------------------------
2011-Oct-06 黄长浩 初始版本
*/
#ifndef __EEPROM_H__
#define __EEPROM_H__
#define STC_EEPROM_START_ADDR 0x2000 //STC89C52RC片内EEPROM起始地址
unsigned char eepromRead( unsigned int address );
void eepromWrite( unsigned int address, unsigned char writeData );
void eepromEraseSector( unsigned int address );
#endif
.c文件
/*
When Who Remarks
----------------------------------
2011-Oct-06 黄长浩 初始版本
*/
sfr isp_data=0xe2;
sfr isp_addrh=0xe3;
sfr isp_addrl=0xe4;
sfr isp_cmd=0xe5;
sfr isp_trig=0xe6;
sfr isp_contr=0xe7;
//擦除片内EEPROM的一个扇区
//擦除只能以扇区为最小单位进行,没法只擦除一个字节
//一个扇区是512个字节
//本函数参数里面的地址落在哪个扇区,则该扇区内数据都将被擦除
//例如:STC89C51RC片内EEPROM第一扇区开始地址为0x2000,结束地址为0x21ff
//如果调用 eepromEraseSector(0x2001),则第一扇区内数据都将被擦除
//擦除成功后,该扇区内各字节都将变为0xff
void eepromEraseSector (unsigned int address)
{
unsigned char i;
isp_addrl=address;
isp_addrh=address>>8;
isp_contr=0x01;
isp_contr=isp_contr|0x81; // 0x80 if SYSCLK<40MHz, 0x81 if SYSCLK<20MHz, 0x82 if SYSCLK<10MHz, 0x83 if SYSCLK<5MHz
isp_cmd=0x03;
isp_trig=0x46;
isp_trig=0xb9;
for(i=0;i<3;i++);
isp_addrl=0xff;
isp_addrh=0xff;
isp_contr=0x00;
isp_cmd=0x00;
isp_trig=0x00;
}
//对STC片内EEPROM的指定地址写入数据(即,字节编程)。
//注意:字节编程是指将eeprom的1写成1或0,将0写成0,而无法将0写成1
//所以,在写入数据前,一定要用扇区擦除将所有字节变为0xff
void eepromWrite(unsigned int address, unsigned char write_data)
{
unsigned char i;
isp_data=write_data;
isp_addrl=address;
isp_addrh=address>>8;
isp_contr=0x01;
isp_contr=isp_contr|0x81; // 0x80 if SYSCLK<40MHz, 0x81 if SYSCLK<20MHz, 0x82 if SYSCLK<10MHz, 0x83 if SYSCLK<5MHz
isp_cmd=0x02;
isp_trig=0x46;
isp_trig=0xb9;
for(i=0;i<3;i++);
isp_addrl=0xff;
isp_addrh=0xff;
isp_contr=0x00;
isp_cmd=0x00;
isp_trig=0x00;
}
//读取STC单片机内部EEPROM的一个字节
//主要不同的STC单片机EEPROM起始地址不同
//例如:STC89c52RC的片内EEPROM起始地址为0x2000
unsigned char eepromRead(unsigned int address)
{
unsigned char i,z;
isp_addrl=address;
isp_addrh=address>>8;
isp_contr=0x01;
isp_contr=isp_contr|0x81; // 0x80 if SYSCLK<40MHz, 0x81 if SYSCLK<20MHz, 0x82 if SYSCLK<10MHz, 0x83 if SYSCLK<5MHz
isp_cmd=0x01;
isp_trig=0x46;
isp_trig=0xb9;
for(i=0;i<3;i++);
isp_addrl=0xff;
isp_addrh=0xff;
isp_contr=0x00;
isp_cmd=0x00;
isp_trig=0x00;
z=isp_data;
return(z);
}
串口及主程序
串口中断函数
void ser() interrupt 4
{
if(RI)
{
getdata[flag] = SBUF;
RI=0;
}
flag++;
if(getdata[1] ==0x03)
{
a = 3;
}
else if(getdata[1] == 0x06)
{
a = getdata[2]+3;
}
else
{
a = 10;
}
if(flag >= a)
{
num = 1;
flag =0;
}
}
触发中断以后接收数据,根据接收到的数据标志来判断上位机要传递到单片机多少个字节的数据。
串口发送
void put(uchar *ff,lenth)
{
for(i=0;i<lenth;i++)
{
SBUF=ff[i];
while(!TI);
delay(10);
TI=0;
}
}
void delay(uint i)
{
uchar j;
for(j=0;j<i;j++);
}
参数是数组和整数,但是不知道为啥,我的板子,再每一帧数据发送完成时不加一个小延时的话,会乱码。
剩下的就是读和写eeprom了,根据接收到的数据,做相应的处理即可。
程序
#include <reg51.h>
#include"eeprom.h"
#define uchar unsigned char
#define uint unsigned int
uchar num,a=0,i,flag=0;
uchar xdata getdata[30] = {0}; //串口接收到的数据缓存
uchar xdata datacache[30] = {0}; //响应解析串口数据后,要返回的数据。 根据自己要读写的字节数量来扩大数组的容量。
uint datalenth;
uchar ok[4] = {0x21,0x22,0x23,0x24}; //初始化就会先行写入的数据。
/* 0x01 0x06 0x00 0x0f 0x66 0x77 ...
51address dataflag datalenth data
*/
void delay(uint i)
{
uchar j;
for(j=0;j<i;j++);
}
void put(uchar *ff,lenth)
{
for(i=0;i<lenth;i++)
{
SBUF=ff[i];
while(!TI);
delay(10);
TI=0;
}
}
void init()
{
TMOD=0x20;
SCON=0x50;
EA=1;
ES=1;
TR1=1;
TH1=0xfd;
TL1=0xfd;
}
void readdata()
{
uint address = 0x2000,i;
for(i=0;i<datalenth;i++)
{
datacache[i] = eepromRead(address);
address+=1;
}
}
void w(uchar *cache,len)
{
uint address1 = 0x2000;
uchar i;
eepromEraseSector(address1);
for(i=0;i<len;i++)
{
eepromWrite(address1+i,cache[i]);
}
}
void main()
{
uchar j;
init();
w(ok,4);
while(1)
{
if(num==1 & getdata[0]==0x01) //瓚剿岆瘁衄揹諳杅擂腔換冞
{
datalenth = getdata[2];
ES=0;
if(getdata[1] == 0x03)
{
readdata();
}
if(getdata[1] == 0x06)
{
for(j=0;j<datalenth;j++)
{
datacache[j] = getdata[j+3];
}
w(datacache,datalenth);
}
put(datacache,datalenth);
num=0;
ES=1;
}
}
}
void ser() interrupt 4
{
if(RI)
{
getdata[flag] = SBUF;
RI=0;
}
flag++;
if(getdata[1] ==0x03)
{
a = 3;
}
else if(getdata[1] == 0x06)
{
a = getdata[2]+3;
}
else
{
a = 10;
}
if(flag >= a)
{
num = 1;
flag =0;
}
}
…
- 程序大概就是这样,串口程序相对来说是实现了简单的多字节数据发送以及接收,变相的可以做模仿工业的modbus rtu数据协议,不过要改的会很大。