找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1602|回复: 12
收起左侧

有大神帮我改一下单片机代码吗? eeprom的按字节写入

[复制链接]
ID:1130333 发表于 2024-10-22 20:15 | 显示全部楼层 |阅读模式
首先定义一个sbit cap=P32;
P32 连接一个22uf电容,然后接地.,上电后 P32在 0.5秒后,充满电应该是属于低电平,(P32=0)
在这0.5秒内,应该属于高电平(P32=1).
那我直接判断 if(P32==1) 那就写eeprom(按字节写入), 如果P32==0 那就读取上一次写的eeprom

这不就实现我的需求了吗?  按照这个逻辑,我自己写了一个代码 .但是老是有问题.

单片机源程序如下:
  1. #include "stc8g.h"
  2. #include "intrins.h"
  3. #include <stdio.h>

  4. #define MAIN_Fosc 11059200UL
  5. #define BRT         (65536 - MAIN_Fosc / 115200 / 4)

  6. typedef unsigned char u8;
  7. typedef unsigned int u16;

  8. u8 dat;                                                //定义eeprom 储存数据,但实际好像没用上
  9. u16 EEPROMId = 0x0020;                                //定义eeprom的地址

  10. sbit cap = P3^2;                                        //p32引脚连电容,接地 做电平判定

  11. void sys_init();                                        //声明系统初始化配置
  12. void PWM_init(void);                                //声明pwm

  13. void main()
  14. {               
  15.         unsigned char x;                                 //定义变量x 用于读取eeprom
  16.         unsigned char y=5;                        //定义变量y用于保存pwm值
  17.         sys_init();                                  //调用系统配置

  18.           P3M0 = 0x00; P3M1 = 0x00;                //P32 输出对电容充电
  19.           P5M0 = 0x00; P5M1 = 0x00;                   //pwm 设置在p55口

  20.         x = IapRead(EEPROMId);                        //读取的当前eeprom 赋值给x        
  21.         
  22.           while(1)
  23.         {         
  24.                 if(cap)                                //如果p32引脚 充电中 p32==1
  25.                 {
  26.                 IapProgram(EEPROMId,y);                //写地址为0x0020 为 y(以后设置为switch或者数组)

  27.                 EEPROMId=EEPROMId+1;                //下次写eeprom +1  0x0021 ,在下次0x0022 以此类推
  28.                 y=y+1;                                //pwm亮度 数组+1   (1,15,55,255)
  29.          
  30.                 }
  31.                  CCAP2H=x;                                //把x写入的值 赋值给pwm

  32.                 if(EEPROMId >= 0X1FE)                                //如果当前字节到达EEPROM末尾,擦除扇区数据
  33.                  {        
  34.                           IapErase(0x0020);                                //擦除扇区
  35.                  
  36.                         EEPROMId = 0x0020;                        //从头开始按字节写入
  37.                  }
  38.         }
  39. }

  40. void sys_init(void)
  41. {
  42.         P_SW2=0x80;
  43.       PWM_init();
  44.         EA=1;                        //打开总中断
  45. }


  46. //// 初始化PWM功能 P55 pwm
  47. void PWM_init(void)
  48. {
  49.     P_SW1=0x20;
  50.     CCON = 0x00;
  51.     CMOD = 0x08;
  52.     CL = 0x00;
  53.     CH = 0x00;

  54.     CCAPM2 = 0x42;
  55.     PCA_PWM2 = 0x00;
  56.     CCAP2L = 0x00;                  
  57.     CCAP2H = 0x00;
  58.     CR=1;        
  59. }

  60. void delayms(u16 ms)
  61. {
  62.         unsigned int i;
  63.         do{
  64.                 i = MAIN_Fosc /10000;
  65.                 while(--i);
  66.         }while(--ms);
  67. }

  68. void IapIdle()
  69. {
  70.         IAP_CONTR = 0;                                                 // 关闭 IAP 功能
  71.         IAP_CMD = 0;                                                         // 清除命令寄存器
  72.         IAP_TRIG = 0;                                                 // 清除触发寄存器

  73.         IAP_ADDRH = 0x00;                                        // 清零高地址寄存器
  74.         IAP_ADDRL = 0x00;                                        // 清零低地址寄存器
  75. }

  76. char IapRead(unsigned int addr)
  77. {
  78.         
  79.         char dat;
  80.         IAP_CONTR = 0x80;                                 // 使能 IAP
  81.         IAP_TPS = 12;                                                 // 设置等待参数 12MHz
  82.         IAP_CMD = 1;                                                         // 设置 IAP 读命令
  83.         IAP_ADDRL = addr;                                 // 设置 IAP 低地址
  84.         IAP_ADDRH = addr >> 8;                 // 设置 IAP 高地址
  85.         EA=0;
  86.         _nop_();
  87.         _nop_();        
  88.         IAP_TRIG = 0x5a;                                         // 写触发命令 (0x5a)
  89.         IAP_TRIG = 0xa5;                                         // 写触发命令 (0xa5)
  90.         EA=1;
  91.         _nop_();
  92.         _nop_();
  93.         _nop_();
  94.         _nop_();
  95.         dat = IAP_DATA;                                         // 读 IAP 数据
  96.         IapIdle();                                                                 // 关闭 IAP 功能
  97.         return dat;

  98. }
  99. void IapProgram(unsigned int addr, char dat)
  100. {

  101.         IAP_CONTR = 0x80;                                 // 使能 IAP
  102.         IAP_TPS = 12;                                                 // 设置等待参数 12MHz
  103.         IAP_CMD = 2;                                                        // 设置 IAP 写命令
  104.         IAP_ADDRL = addr;                                 // 设置 IAP 低地址
  105.         IAP_ADDRH = addr >> 8;                 // 设置 IAP 高地址

  106.         IAP_DATA = dat;                                         // 写 IAP 数据
  107.         EA=0;
  108.         _nop_();
  109.         _nop_();        
  110.         IAP_TRIG = 0x5a;                                         // 写触发命令 (0x5a)
  111.         IAP_TRIG = 0xa5;                                         // 写触发命令 (0xa5)
  112.         EA=1;        
  113.         _nop_();
  114.         _nop_();
  115.         _nop_();
  116.         _nop_();
  117.         IapIdle();                                                                // 关闭 IAP 功能

  118. }
  119. void IapErase(unsigned int addr)
  120. {

  121.         IAP_CONTR = 0x80;                                 // 使能 IAP
  122.         IAP_TPS = 12;                                                 // 设置等待参数 12MHz
  123.         IAP_CMD = 3;                                                         // 设置 IAP 擦除命令
  124.         IAP_ADDRL = addr;                                 // 设置 IAP 低地址
  125.         IAP_ADDRH = addr >> 8;                 // 设置 IAP 高地址

  126.         EA=0;
  127.         _nop_();
  128.         _nop_();        
  129.         IAP_TRIG = 0x5a;                                         // 写触发命令 (0x5a)
  130.         IAP_TRIG = 0xa5;                                         // 写触发命令 (0xa5)
  131.         EA=1;        
  132.         _nop_();
  133.         _nop_();
  134.         _nop_();
  135.         _nop_();
  136.         IapIdle();                                                                 // 关闭 IAP 功能

  137. }
复制代码
回复

使用道具 举报

ID:57657 发表于 2024-10-22 22:34 | 显示全部楼层
字节读:可使用unsigned char/int/long code指针汇编成MOVC指令即可访问,不需要配置EEPROM寄存器,字节写将二进制1写为0且不可逆,必须扇区擦除才能恢复,没有字节擦除,擦除后全部为0xFF(二进制1),一次擦512字节。
回复

使用道具 举报

ID:1130333 发表于 2024-10-22 23:07 | 显示全部楼层
npn 发表于 2024-10-22 22:34
字节读:可使用unsigned char/int/long code指针汇编成MOVC指令即可访问,不需要配置EEPROM寄存器,字节写将 ...

可以帮忙完善下我的代码吗?
512 个字节,,
从0可以写,每次小于0.5秒的关,开机,都写一次新数据.
写到510 就擦除这个扇区,然后再从 0 开始写.
回复

使用道具 举报

ID:1133081 发表于 2024-10-23 06:10 | 显示全部楼层
P3M0 = 0x00; P3M1 = 0x00;配置是准双向高电平,P32 连接一个22uf电容,然后接地,在上电后短暂时间内P32电压由0逐渐上升到接近VCC。从一般意义讲,端口电压小于1/3VCC判断为低电平,大于2/3VCC判断为高电平。中间为不确定状态(因芯片制造工艺不同,其电气特性有差异,要以其用户手册为准)。楼主以此方法取样不妥。再者楼主在主循环中频繁读写EEPROM也是忌讳。通常做法是设置一个变量,掉电时保存这个变量的数据在EEPROM中,再次上电初始化时读取保存的数据赋值这个变量。
回复

使用道具 举报

ID:143767 发表于 2024-10-23 10:05 | 显示全部楼层
上电瞬间是低电平,充满电后是高电平,你正好理解反了
回复

使用道具 举报

ID:1130333 发表于 2024-10-23 12:49 | 显示全部楼层
WL0123 发表于 2024-10-23 06:10
P3M0 = 0x00; P3M1 = 0x00;配置是准双向高电平,P32 连接一个22uf电容,然后接地,在上电后短暂时间内P32电 ...

可以帮忙完善下吗?
回复

使用道具 举报

ID:1130333 发表于 2024-10-23 12:49 | 显示全部楼层
dj3365191 发表于 2024-10-23 10:05
上电瞬间是低电平,充满电后是高电平,你正好理解反了

可以帮忙完善下代码吗?
回复

使用道具 举报

ID:69038 发表于 2024-10-23 16:41 | 显示全部楼层
如果是“上电后 P32在 0.5秒后,充满电应该是属于低电平”,充满电后应是高电平的才对吧?
回复

使用道具 举报

ID:1130333 发表于 2024-10-24 10:19 | 显示全部楼层
关键是eeprom单字节写入.
这个没问题
回复

使用道具 举报

ID:1109793 发表于 2024-10-25 10:24 | 显示全部楼层
0.5秒关机再开机,开机后充电,此时单片机不一定能复位完成,关机后0.5秒,此时电容的电未必能放完。再开机不一定能及时复位并开始运行你的代码。搞不懂为啥要这么操作。
回复

使用道具 举报

ID:624769 发表于 2024-10-25 20:54 | 显示全部楼层
你是那个要做手电筒的吧? 你的思路是不是搞错了?
正确的思路应该是:
给单片机提供足够大的电容,确保单片机在 0.5S 内不会被完全放电,当单片机检测到“断电”这个信号之后,给标志位F0置位1,然后设置 0.5秒的定时换醒后 休眠。 0.5秒后休眠唤醒 把F0清0继续休眠。如果0.5秒内被来电唤醒,因为标志F0 为1 则模式增加1,同时跟新 Eeprom.  如果 0.5秒后被唤新,或者,时间很长彻底掉电了,重新上电,因为F0都为0 则 读取Eeprom 数据,继续使用,原来的模式。
回复

使用道具 举报

ID:1133081 发表于 2024-10-26 06:30 | 显示全部楼层
vb2002 发表于 2024-10-23 12:49
可以帮忙完善下吗?

首先要明确表明你的需求是什么,才能提供有效的解决方案。
回复

使用道具 举报

ID:1130333 发表于 2024-10-29 23:36 来自触屏版 | 显示全部楼层
188610329 发表于 2024-10-25 20:54
你是那个要做手电筒的吧? 你的思路是不是搞错了?
正确的思路应该是:
给单片机提供足够大的电容,确保 ...

手电功能上已经搞定了,
现在就是每次都是写 1字节,然后擦除整个扇区,
觉得这样太浪费了,eeprom寿命怕顶不住多久,
想弄成 每次读一字节,写一字节,写完 512或者 1024,2048,再擦除扇区,再从头开始写
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表