当前位置: 首页 > news >正文

STC15系列单片机通过串口多字节数据读写EEPROM操作

STC15系列单片机通过串口多字节数据读写EEPROM操作


  • 📌相关篇《STC15系列单片机EEPROM读写示例》

⛳手册勘误信息注意事项

在这里插入图片描述

  • ⚡在手册上面描述STC15F2K60S2及STC15L2K60S2系列单片机内部EEPROM还可以用MOVC指令读,但此时首地址不再是0000H,而是程序存储空间结束地址的下一个地址。
  • 🔰实际上STC15F2K60S2型号单片机验证时发现,EEPROM起始扇区首地址:0x0000 - 0x0200 2个扇区(1K=512X2),而不是像手册上描述的在CODE区后面的2个扇区。
  • ⚡对于IAP型号的单片机,EEPROM扇区在CODE(程序区)后面。
    在这里插入图片描述
  • 例如上面的程序编译信息,可以得知程序所占用的容量code=551,转换为扇区,就是需要占用2个扇区(2 X 512),对于IAP单片机,整个ROM区都可以作为eeprom使用,在不覆盖程序的情况下,从第3扇区作为eeprom的首地址:0x600

📝STC15系列单片机通过串口读写EEPROM示例

  • ✨这里以 STC15F2K60S2为烧录对象为例。采用22.1184MHz,波特率:115200
  • 程序代码
/*---------------------------------------------------------------------*/
/* --- STC MCU International Limited ----------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.GXWMCU.com --------------------------------------------*/
/* --- QQ:  800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了宏晶科技的资料及程序   */
/*---------------------------------------------------------------------*//*************	本程序功能说明	**************测试说明通过串口对STC内部自带的EEPROM(FLASH)进行读写测试。对FLASH做扇区擦除、写入、读出的操作,命令指定地址。默认波特率:  115200,8,N,1. 
默认主时钟:  22118400HZ.串口命令设置: (命令字母不区分大小写)W 0x8000 1234567890  --> 对0x8000地址写入字符1234567890.R 0x8000 10          --> 对0x8000地址读出10个字节数据. 注意:为了通用,程序不识别地址是否有效,用户自己根据具体的型号来决定。******************************************/#define 	MAIN_Fosc			22118400L	//定义主时钟
#include	"STC15Fxxxx.H"#define		Baudrate1			115200L
#define		Tmp_Length			70		//读写EEPROM缓冲长度#define		UART1_BUF_LENGTH	(Tmp_Length+9)	//串口缓冲长度u8	RX1_TimeOut;
u8	TX1_Cnt;	//发送计数
u8	RX1_Cnt;	//接收计数
bit	B_TX1_Busy;	//发送忙标志u8 	xdata 	RX1_Buffer[UART1_BUF_LENGTH];	//接收缓冲
u8	xdata	tmp[Tmp_Length];		//EEPROM操作缓冲void	UART1_config(u8 brt);	// 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
void	PrintString1(u8 *puts);
void	UART1_TxByte(u8 dat);
void	delay_ms(u8 ms);
u8		CheckData(u8 dat);
u16		GetAddress(void);
u8		GetDataLength(void);
void	EEPROM_SectorErase(u16 EE_address);
void 	EEPROM_read_n(u16 EE_address,u8 *DataAddress,u8 length);
u8		EEPROM_write_n(u16 EE_address,u8 *DataAddress,u8 length);/********************* 主函数 *************************/
void main(void)
{u8	i,j;u16 addr;u8	status;P0M1 = 0;	P0M0 = 0;	//设置为准双向口P1M1 = 0;	P1M0 = 0;	//设置为准双向口P2M1 = 0;	P2M0 = 0;	//设置为准双向口P3M1 = 0;	P3M0 = 0;	//设置为准双向口P4M1 = 0;	P4M0 = 0;	//设置为准双向口P5M1 = 0;	P5M0 = 0;	//设置为准双向口P6M1 = 0;	P6M0 = 0;	//设置为准双向口P7M1 = 0;	P7M0 = 0;	//设置为准双向口UART1_config(1);	// 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.EA = 1;	//允许总中断PrintString1("STC15F2K60S2系列单片机EEPROM测试程序, 串口命令设置如下示例:\r\n");	//SUART1发送一个字符串PrintString1("W 0x8000 1234567890  --> 对0x8000地址写入字符1234567890.\r\n");	//SUART1发送一个字符串PrintString1("R 0x8000 10          --> 对0x8000地址读出10个字节数据.\r\n");	//SUART1发送一个字符串while(1){delay_ms(1);if(RX1_TimeOut > 0)		//超时计数{if(--RX1_TimeOut == 0){//		for(i=0; i<RX1_Cnt; i++)	UART1_TxByte(RX1_Buffer[i]);	//把收到的数据原样返回,用于测试status = 0xff;	//状态给一个非0值if((RX1_Cnt >= 10) && (RX1_Buffer[1] == ' ') && (RX1_Buffer[8] == ' '))	//最短命令为10个字节{for(i=0; i<8; i++){if((RX1_Buffer[i] >= 'a') && (RX1_Buffer[i] <= 'z'))	RX1_Buffer[i] = RX1_Buffer[i] - 'a' + 'A';	//小写转大写}addr = GetAddress();if(addr < 63488)	//限制在0~123扇区{if(RX1_Buffer[0] == 'W')	//写入N个字节{j = RX1_Cnt - 9;if(j > Tmp_Length)	j = Tmp_Length;	//越界检测EEPROM_SectorErase(addr);			//擦除扇区i = EEPROM_write_n(addr,&RX1_Buffer[9],j);		//写N个字节if(i == 0){PrintString1("已写入");if(j >= 100)	{UART1_TxByte(j/100+'0');	j = j % 100;}if(j >= 10)		{UART1_TxByte(j/10+'0');	j = j % 10;}UART1_TxByte(j%10+'0');PrintString1("字节数据!\r\n");}else	PrintString1("写入错误!\r\n");status = 0;	//命令正确}else if(RX1_Buffer[0] == 'R')	//PC请求返回N字节EEPROM数据{j = GetDataLength();if(j > Tmp_Length)	j = Tmp_Length;	//越界检测if(j > 0){PrintString1("读出");UART1_TxByte(j/10+'0');UART1_TxByte(j%10+'0');PrintString1("个字节数\xfd据如下:\r\n");EEPROM_read_n(addr,tmp,j);for(i=0; i<j; i++)	UART1_TxByte(tmp[i]);UART1_TxByte(0x0d);UART1_TxByte(0x0a);status = 0;	//命令正确}}}}if(status != 0)	PrintString1("命令错误!\r\n");RX1_Cnt  = 0;	//清除字节数}}}
}
//========================================================================//========================================================================
// 函数: void  delay_ms(unsigned char ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数, 这里只支持1~255ms. 自动适应主时钟.
// 返回: none.
// 版本: VER1.0
// 日期: 2013-4-1
// 备注: 
//========================================================================
void  delay_ms(u8 ms)
{unsigned int i;do{i = MAIN_Fosc / 14000;while(--i)	;   //14T per loop}while(--ms);
}//========================================================================
// 函数: u8	CheckData(u8 dat)
// 描述: 将字符"0~9,A~F或a~f"转成十六进制.
// 参数: dat: 要检测的字符.
// 返回: 0x00~0x0F为正确. 0xFF为错误.
// 版本: V1.0, 2012-10-22
//========================================================================
u8	CheckData(u8 dat)
{if((dat >= '0') && (dat <= '9'))		return (dat-'0');if((dat >= 'A') && (dat <= 'F'))		return (dat-'A'+10);return 0xff;
}//========================================================================
// 函数: u16	GetAddress(void)
// 描述: 计算各种输入方式的地址.
// 参数: 无.
// 返回: 16位EEPROM地址.
// 版本: V1.0, 2013-6-6
//========================================================================
u16	GetAddress(void)
{u16	address;u8	i,j;address = 0;if((RX1_Buffer[2] == '0') && (RX1_Buffer[3] == 'X')){for(i=4; i<8; i++){j = CheckData(RX1_Buffer[i]);if(j >= 0x10)	return 65535;	//erroraddress = (address << 4) + j;}return (address);}return	65535;	//error
}/**************** 获取要读出数据的字节数 ****************************/
u8	GetDataLength(void)
{u8	i;u8	length;length = 0;for(i=9; i<RX1_Cnt; i++){if(CheckData(RX1_Buffer[i]) >= 10)	break;length = length * 10 + CheckData(RX1_Buffer[i]);}return (length);
}//========================================================================
// 函数: void PrintString1(u8 *puts)
// 描述: 串口1发送字符串函数。
// 参数: puts:  字符串指针.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注: 
//========================================================================
void PrintString1(u8 *puts)	//发送一个字符串
{for (; *puts != 0;	puts++)	UART1_TxByte(*puts);  	//遇到停止符0结束
}//========================================================================
// 函数: SetTimer2Baudraye(u16 dat)
// 描述: 设置Timer2做波特率发生器。
// 参数: dat: Timer2的重装值.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注: 
//========================================================================
void	SetTimer2Baudraye(u16 dat)	// 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{AUXR &= ~(1<<4);	//Timer stopAUXR &= ~(1<<3);	//Timer2 set As TimerAUXR |=  (1<<2);	//Timer2 set as 1T modeTH2 = dat / 256;TL2 = dat % 256;IE2  &= ~(1<<2);	//禁止中断AUXR |=  (1<<4);	//Timer run enable
}//========================================================================
// 函数: void	UART1_config(u8 brt)
// 描述: UART1初始化函数。
// 参数: brt: 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注: 
//========================================================================
void	UART1_config(u8 brt)	// 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
{/*********** 波特率使用定时器2 *****************/if(brt == 2){AUXR |= 0x01;		//S1 BRT Use Timer2;SetTimer2Baudraye(65536UL - (MAIN_Fosc / 4) / Baudrate1);}/*********** 波特率使用定时器1 *****************/else{TR1 = 0;AUXR &= ~0x01;		//S1 BRT Use Timer1;AUXR |=  (1<<6);	//Timer1 set as 1T modeTMOD &= ~(1<<6);	//Timer1 set As TimerTMOD &= ~0x30;		//Timer1_16bitAutoReload;TH1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) / 256);TL1 = (u8)((65536UL - (MAIN_Fosc / 4) / Baudrate1) % 256);ET1 = 0;	//禁止中断INT_CLKO &= ~0x02;	//不输出时钟TR1  = 1;}/*************************************************/SCON = (SCON & 0x3f) | 0x40;	//UART1模式, 0x00: 同步移位输出, 0x40: 8位数据,可变波特率, 0x80: 9位数据,固定波特率, 0xc0: 9位数据,可变波特率
//	PS  = 1;	//高优先级中断ES  = 1;	//允许中断REN = 1;	//允许接收P_SW1 &= 0x3f;P_SW1 |= 0x00;		//UART1 switch to, 0x00: P3.0 P3.1, 0x40: P3.6 P3.7, 0x80: P1.6 P1.7 (必须使用内部时钟)
//	PCON2 |=  (1<<4);	//内部短路RXD与TXD, 做中继, ENABLE,DISABLEB_TX1_Busy = 0;TX1_Cnt = 0;RX1_Cnt = 0;RX1_TimeOut = 0;
}//========================================================================
// 函数: void	UART1_TxByte(u8 dat)
// 描述: 发送一个字节.
// 参数: 无.
// 返回: 无.
// 版本: V1.0, 2014-6-30
//========================================================================void	UART1_TxByte(u8 dat)
{SBUF = dat;B_TX1_Busy = 1;while(B_TX1_Busy);
}//========================================================================
// 函数: void UART1_int (void) interrupt UART1_VECTOR
// 描述: UART1中断函数。
// 参数: nine.
// 返回: none.
// 版本: VER1.0
// 日期: 2014-11-28
// 备注: 
//========================================================================
void UART1_int (void) interrupt UART1_VECTOR
{if(RI){RI = 0;if(RX1_Cnt >= UART1_BUF_LENGTH)	RX1_Cnt = 0;RX1_Buffer[RX1_Cnt] = SBUF;RX1_Cnt++;RX1_TimeOut = 5;}if(TI){TI = 0;B_TX1_Busy = 0;}
}/*
STC15F/L2KxxS2	扇区分配,512字节/扇区,从0x0000开始。型号        大小   扇区数  开始地址  结束地址   MOVC读偏移地址
STC15F/L2K08S2   53K   106扇区  0x0000  ~  0xD3FF        0x2000
STC15F/L2K16S2   45K    90扇区  0x0000  ~  0xB3FF        0x4000
STC15F/L2K24S2   37K    74扇区  0x0000  ~  0x93FF        0x6000
STC15F/L2K32S2   29K    58扇区  0x0000  ~  0x73FF        0x8000
STC15F/L2K40S2   21K    42扇区  0x0000  ~  0x53FF        0xA000
STC15F/L2K48S2   13K    26扇区  0x0000  ~  0x33FF        0xC000
STC15F/L2K56S2   5K     10扇区  0x0000  ~  0x13FF        0xE000
STC15F/L2K60S2   1K      2扇区  0x0000  ~  0x03FF        0xF000STC15F/L2K61S2   无EPROM, 整个122扇区的FLASH都可以擦写 地址 0x0000~0xF3ff.
*/#define		ISP_ENABLE()	ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY)
#define		ISP_DISABLE()	ISP_CONTR = 0; ISP_CMD = 0; ISP_TRIG = 0; ISP_ADDRH = 0xff; ISP_ADDRL = 0xff//========================================================================
// 函数: void DisableEEPROM(void)
// 描述: 禁止EEPROM.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void DisableEEPROM(void)		//禁止访问EEPROM
{ISP_CONTR = 0;				//禁止ISP/IAP操作ISP_CMD   = 0;				//去除ISP/IAP命令ISP_TRIG  = 0;				//防止ISP/IAP命令误触发ISP_ADDRH = 0xff;			//指向非EEPROM区,防止误操作ISP_ADDRL = 0xff;			//指向非EEPROM区,防止误操作
}//========================================================================
// 函数: void	EEPROM_Trig(void)
// 描述: 触发EEPROM操作.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void	EEPROM_Trig(void)
{F0 = EA;	//保存全局中断EA = 0;		//禁止中断, 避免触发命令无效ISP_TRIG();							//先送5AH,再送A5H到ISP/IAP触发寄存器,每次都需要如此//送完A5H后,ISP/IAP命令立即被触发启动//CPU等待IAP完成后,才会继续执行程序。_nop_();_nop_();EA = F0;	//恢复全局中断
}//========================================================================
// 函数: void	EEPROM_SectorErase(u16 EE_address)
// 描述: 擦除一个扇区.
// 参数: EE_address:  要擦除的EEPROM的扇区中的一个字节地址.
// 返回: none.
// 版本: V1.0, 2014-6-30
//========================================================================
void	EEPROM_SectorErase(u16 EE_address)
{ISP_ENABLE();						//设置等待时间,允许ISP/IAP操作,送一次就够IAP_CONTR = 0X83;ISP_ERASE();						//宏调用, 送扇区擦除命令,命令不需改变时,不需重新送命令//只有扇区擦除,没有字节擦除,512字节/扇区。//扇区中任意一个字节地址都是扇区地址。ISP_ADDRH = EE_address / 256;		//送扇区地址高字节(地址需要改变时才需重新送地址)ISP_ADDRL = EE_address % 256;		//送扇区地址低字节EEPROM_Trig();						//触发EEPROM操作DisableEEPROM();					//禁止EEPROM操作
}//========================================================================
// 函数: void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u8 lenth)
// 描述: 读N个字节函数.
// 参数: EE_address:  要读出的EEPROM的首地址.
//       DataAddress: 要读出数据的指针.
//       length:      要读出的长度
// 返回: 0: 写入正确.  1: 写入长度为0错误.  2: 写入数据错误.
// 版本: V1.0, 2014-6-30
//========================================================================
void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u8 length)
{ISP_ENABLE();							//设置等待时间,允许ISP/IAP操作,送一次就够IAP_CONTR = 0X83;ISP_READ();								//送字节读命令,命令不需改变时,不需重新送命令do{ISP_ADDRH = EE_address / 256;		//送地址高字节(地址需要改变时才需重新送地址)ISP_ADDRL = EE_address % 256;		//送地址低字节EEPROM_Trig();						//触发EEPROM操作*DataAddress = ISP_DATA;			//读出的数据送往EE_address++;DataAddress++;}while(--length);DisableEEPROM();
}//========================================================================
// 函数: u8 EEPROM_write_n(u16 EE_address,u8 *DataAddress,u8 length)
// 描述: 写N个字节函数.
// 参数: EE_address:  要写入的EEPROM的首地址.
//       DataAddress: 要写入数据的指针.
//       length:      要写入的长度
// 返回: 0: 写入正确.  1: 写入长度为0错误.  2: 写入数据错误.
// 版本: V1.0, 2014-6-30
//========================================================================
u8 	EEPROM_write_n(u16 EE_address,u8 *DataAddress,u8 length)
{u8	i;u16	j;u8	*p;if(length == 0)	return 1;	//长度为0错误ISP_ENABLE();						//设置等待时间,允许ISP/IAP操作,送一次就够i = length;j = EE_address;p = DataAddress;ISP_WRITE();							//宏调用, 送字节写命令do{ISP_ADDRH = EE_address / 256;		//送地址高字节(地址需要改变时才需重新送地址)ISP_ADDRL = EE_address % 256;		//送地址低字节ISP_DATA  = *DataAddress;			//送数据到ISP_DATA,只有数据改变时才需重新送EEPROM_Trig();						//触发EEPROM操作EE_address++;						//下一个地址DataAddress++;						//下一个数据}while(--length);						//直到结束EE_address = j;length = i;DataAddress = p;i = 0;ISP_READ();								//读N个字节并比较do{ISP_ADDRH = EE_address / 256;		//送地址高字节(地址需要改变时才需重新送地址)ISP_ADDRL = EE_address % 256;		//送地址低字节EEPROM_Trig();						//触发EEPROM操作if(*DataAddress != ISP_DATA)		//读出的数据与源数据比较{i = 2;break;}EE_address++;DataAddress++;}while(--length);DisableEEPROM();return i;
}

🛠串口写入和读取操作

  • 🌿写入操作:W 0x0200 abcd9527
    在这里插入图片描述
  • 🌿读取操作:R 0x0200 14
    在这里插入图片描述
  • 🍁在第一个扇区写入数据:W 0x0000 去年今日此门中,人面桃花相映红。人面不知何处去,桃花依旧笑春风。
    在这里插入图片描述
  • 🍁读取第一个扇区数据:R 0x0000 70
    在这里插入图片描述

📚程序源码

  • 🔖本案例来源于STC实验箱4中的案例修改而来。
复制这段内容后打开百度网盘手机App,操作更方便哦
链接: https://pan.baidu.com/s/1Q5k8FONsXFhnaDBcE3jiOw
提取码: dhye
http://www.lryc.cn/news/7444.html

相关文章:

  • 计算机网络-ip数据报
  • 从零开始学C
  • 【云原生】手把手带你从零开始搭建kubernetes最新版本实战
  • trivy os软件包扫描原理分析
  • 算法训练营 day48 动态规划 完全背包 零钱兑换 II 组合总和 Ⅳ
  • Java 基础(1)—泛型简单使用
  • 内存卡损坏了怎么恢复?
  • Mysql使用规范(纯技术和实战建议)
  • Netty源码解读-EventLoop(二)
  • OSI模型详解
  • Share Creators完成500万美元融资,以工具化手段帮助企业从数字资产管理中解放
  • 几个Base64编码工具,也有蹊跷
  • Python|每日一练|排序|递归|字符串|数组|动态规划|单选记录:以特殊格式处理连续增加的数字|正则表达式匹配|地下城游戏
  • Spring Cloud微服务网关Gateway组件
  • cluster nodes(集群节点)
  • 【Android学习】下载jar慢和gradle慢的情况
  • 下一个排列-力扣31-java
  • 前端面试题
  • jsp游戏门户网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目
  • Git与IDEA强强联合(HTTPS协议连接)
  • leetcode 第二题:两数相加-C语言实现
  • 【人工智能】PTP网络时钟服务器在智能驾驶里的重要性
  • 【蓝桥杯集训3】二分专题(3 / 5)
  • 在成都的哪个培训机构学习Java好呢?
  • 传输层重要协议之UDP协议和TCP协议详解
  • BNB Greenfield 成存储赛道“新贵”,BNB 生态的野心与破局
  • 【SQL开发实战技巧】系列(十六):时间类型操作(上):日、月、年、时、分、秒之差及时间间隔计算
  • JavaScript知识点总结
  • adb命令记录
  • 9.Docker Swarm