久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 13651|回復: 22
收起左側

51單片機入門經驗分享5-STC15W1K16PWM內部EEPROM的使用

  [復制鏈接]
ID:74187 發表于 2018-10-13 08:56 | 顯示全部樓層 |閱讀模式
STC15W1K16PWM內部EEPROM的使用--20181013

     在實際開發中,經常會遇到某些需要斷電需要再次上電記憶的場合,這就需要掉電存儲芯片了,最常用的EEPROM芯片就是AT24C02了,幾乎成了每一塊開發板的標配,但是有些時候,在一些低成本的場合,需要用類EEPROM或者flash來模擬EEPROM進行存儲,AT24C02是可以進行字節擦寫的,STC內部的EEPROM是不可以字節擦寫的,他是按照512byte一個扇區來組織的,如下圖所示,我們開發板選擇的這塊芯片分為了22個扇區。如果你要擦除數據,那么必須要一次性的擦除512字節才可以,這么難用,難用總比沒有強吧,在好多產品上,我都見到過,好多掉電了上電依然保持的參數,有沒有外置EEPROM芯片,只能用內部的或者來模擬了,我們來寫一個程序,程序的結果是記憶上電次數,每上電一次,就累加一次,顯示在數碼管上面,OK,寫好的代碼如下所示:

000013.jpg
  1. /*******************************************************************************
  2. * 文件名: 數碼管顯示上電計數值
  3. * 描  述: 上電計數
  4. * 功  能:數碼管的使用
  5. * 作  者:大核桃 597627977
  6. * 版本號:1.0.1(2018.09.21)
  7. *******************************************************************************/
  8. #include "stc15w.h"//頭文件
  9. #include "intrins.h"

  10. /*******************************************************************************
  11. * 文件名: 重定義
  12. * 描  述:   
  13. * 功  能:
  14. * 作  者:大核桃
  15. * 版本號:1.0.1(2018.09.21)
  16. *******************************************************************************/
  17. typedef unsigned char uint8;
  18. typedef unsigned int  uint16;
  19. typedef unsigned long uint32;

  20. /*******************************************************************************
  21. * 文件名:共陽數碼管真值表
  22. * 描  述:
  23. * 功  能:
  24. * 作  者:大核桃
  25. * 版本號:1.0.1(2018.09.21)
  26. *******************************************************************************/
  27. code uint8 LedChar[] = {
  28.         0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
  29.         0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
  30. };

  31. uint16 counter;        //記憶上電次數,最大65535

  32. /*******************************************************************************
  33. * 文件名:單獨位定義
  34. * 描  述:   
  35. * 功  能:
  36. * 作  者:大核桃
  37. * 版本號:1.0.1(2018.09.21)
  38. *******************************************************************************/
  39. sbit LED0 = P1^0;//第1組LED
  40. sbit LED1 = P1^1;//第2組LED
  41. sbit LED2 = P1^2;//第3組LED                                 
  42. sbit LED3 = P1^3;//第4組LED
  43. sbit LED4 = P1^4;//第5組LED
  44. sbit LED5 = P3^2;//第6組LED
  45. sbit LED6 = P0^0;//第7組LED
  46. sbit LED7 = P0^1;//第8組LED

  47. sbit LEDS1 = P3^3;//數碼管1
  48. sbit LEDS2 = P3^4;//數碼管2
  49. sbit LEDS3 = P3^6;//數碼管3
  50. sbit LEDS4 = P3^7;//數碼管4

  51. /*******************************************************************************
  52. * 文件名:全局變量定義區域
  53. * 描  述:
  54. * 功  能:
  55. * 作  者:大核桃
  56. * 版本號:1.0.1(2017.05.23)
  57. *******************************************************************************/
  58. #define CMD_IDLE    0               //空閑模式
  59. #define CMD_READ    1               //IAP字節讀命令
  60. #define CMD_PROGRAM 2               //IAP字節編程命令
  61. #define CMD_ERASE   3               //IAP扇區擦除命令

  62. #define ENABLE_IAP  0x82            //if SYSCLK<20MHz
  63. /*******************************************************************************
  64. * 文件名:函數前置聲明
  65. * 描  述:   
  66. * 功  能:
  67. * 作  者:大核桃
  68. * 版本號:1.0.1(2017.05.23)
  69. *******************************************************************************/
  70. void Mcu_Port_Init();
  71. void LedScan();
  72. void Delay500ms(); //24MHZ
  73. void Time0_Init();//定時器0
  74. void IapIdle();
  75. uint8 IapReadByte(uint16 addr);
  76. void IapProgramByte(uint16 addr, uint8 dat);
  77. void IapEraseSector(uint16 addr);
  78. #define Delay()                {_nop_();_nop_();_nop_();_nop_();}

  79. /*******************************************************************************
  80. * 文件名
  81. * 描  述: 主函數  
  82. * 功  能:入口
  83. * 作  者:大核桃
  84. * 版本號:1.0.1(2017.05.23)
  85. *******************************************************************************/
  86. void main(void)
  87. {                                       
  88.         counter = IapReadByte(0x0000);//讀取數據
  89.         counter++;//寫

  90.         Mcu_Port_Init();//IO上電初始化
  91.         Time0_Init();

  92.         IapEraseSector(0x0000);//擦除數據
  93.         IapProgramByte(0x0000, counter);//寫入數據


  94.         while(1);
  95. }

  96. /*******************************************************************************
  97. * 文件名:void LedScan()
  98. * 描  述: LED刷新
  99. * 功  能:
  100. * 作  者:大核桃
  101. * 版本號:1.0.1(2017.05.23)
  102. *******************************************************************************/
  103. void LedScan()
  104. {
  105.         static uint8 i = 0;

  106.         P2 = 0Xff;
  107.         switch(i)
  108.         {
  109.                 case 0: LEDS4 = 0;LEDS1 = 1;P2 = LedChar[counter / 1000 % 10];i++;break;
  110.                 case 1: LEDS1 = 0;LEDS2 = 1;P2 = LedChar[counter / 100 % 10];i++;break;
  111.                 case 2: LEDS2 = 0;LEDS3 = 1;P2 = LedChar[counter / 10 % 10];i++;break;
  112.                 case 3: LEDS3 = 0;LEDS4 = 1;P2 = LedChar[counter % 10];i = 0;break;

  113.                 default:break;
  114.         }
  115. }
  116. /*******************************************************************************
  117. * 文件名:void Time0_Init()
  118. * 描  述: 定時器0初始化
  119. * 功  能:10毫秒@11.0592MHz
  120. * 作  者:大核桃
  121. * 版本號:1.0.1(2017.05.23)
  122. *******************************************************************************/
  123. void Time0_Init(void)
  124. {
  125.         AUXR &= 0x7F;                //定時器時鐘12T模式
  126.         TMOD &= 0xF0;                //設置定時器模式
  127.         TMOD |= 0X01;      //確保不干擾其他配置
  128.         TH0 = 0xDC;                //設置定時初值
  129.         TL0 = 0x00;                //設置定時初值
  130.         ET0 = 1;
  131.         TR0 = 1;                //定時器0開始計時
  132.         EA = 1;               
  133. }
  134. /*******************************************************************************
  135. * 文件名:
  136. * 描  述: 中斷函數
  137. * 功  能:10毫秒@11.0592MHz
  138. * 作  者:大核桃
  139. * 版本號:1.0.1(2017.05.23)
  140. *******************************************************************************/
  141. void ET0_IRQHandler() interrupt 1
  142. {
  143.         TH0 = 0xDC;                //設置定時初值
  144.         TL0 = 0x00;                //設置定時初值
  145.         LedScan();               
  146. }
  147. /*******************************************************************************
  148. * 文件名:void Mcu_Port_Init()
  149. * 描  述: io初始化
  150. * 功  能:
  151. * 作  者:大核桃
  152. * 版本號:1.0.1(2017.05.23)
  153. *******************************************************************************/
  154. void Mcu_Port_Init()
  155. {
  156.         //將P0口低二位配置為推挽輸出
  157.         //234567位配置位高阻輸入
  158.         P0M1 = 0xFC;//1111 1100
  159.         P0M0 = 0X03;//0000 0011
  160.         //P0 = 0X01;//第6個
  161.         //P0 = 0X02;//第7個
  162.         //高3位配置高阻輸入,用作模擬口
  163.         //其他配置推挽輸出,驅動LED
  164.         P1M1 = 0xE0;//1110 0000
  165.         P1M0 = 0X1F;//0001 1111
  166.         //P2口配置準雙向口
  167.         P2M1 = 0X00;
  168.         P2M0 = 0X00;
  169.         P2 = 0Xff; //上電為1111 1111

  170. //        //P54,P55口為推挽輸出
  171.         P5M1 = 0X00;
  172.         P5M0 = 0X00;
  173.         P5 = 0xFF;

  174.         //P37,P36,3.2,P3.3 P3.4口為推挽輸出
  175.         P3M1 = 0X00;
  176.         P3M0 = 0XFC;
  177.         P3 = 0X23; //0010 0111//第5個LED端口
  178.         
  179.         LED0 = 0;//第1組LED,如果使能請置為1
  180.         LED1 = 0;
  181.         LED2 = 0;
  182.         LED3 = 0;
  183.         LED4 = 0;
  184.         LED5 = 0;
  185.         LED6 = 0;
  186.         LED7 = 0;        
  187. }

  188. /*******************************************************************************
  189. * 文件名:void Delay500ms()                //@24.000MHz
  190. * 描  述:Y5內核延時
  191. * 功  能:
  192. * 作  者:大核桃
  193. * 版本號:1.0.1(2017.05.23)
  194. *******************************************************************************/
  195. void Delay500ms()                //@24.000MHz
  196. {
  197.         unsigned char i, j, k;

  198.         _nop_();
  199.         _nop_();
  200.         i = 46;
  201.         j = 153;
  202.         k = 245;
  203.         do
  204.         {
  205.                 do
  206.                 {
  207.                         while (--k);
  208.                 } while (--j);
  209.         } while (--i);
  210. }
  211. /*******************************************************************************
  212. * 文件名:void IapIdle()
  213. * 描  述:關閉IAP
  214. * 功  能:
  215. * 作  者:大核桃
  216. * 版本號:1.0.1(2017.05.23)
  217. *******************************************************************************/
  218. void IapIdle()
  219. {
  220.     IAP_CONTR = 0;                  //關閉IAP功能
  221.     IAP_CMD = 0;                    //清除命令寄存器
  222.     IAP_TRIG = 0;                   //清除觸發寄存器
  223.     IAP_ADDRH = 0x80;               //將地址設置到非IAP區域
  224.     IAP_ADDRL = 0;
  225. }
  226. /*******************************************************************************
  227. * 文件名:uint8 IapReadByte(uint16 addr)
  228. * 描  述:從ISP/IAP/EEPROM區域讀取一字節
  229. * 功  能:
  230. * 作  者:大核桃
  231. * 版本號:1.0.1(2017.05.23)
  232. *******************************************************************************/
  233. uint8 IapReadByte(uint16 addr)
  234. {
  235.     uint8 dat;                       //數據緩沖區

  236.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  237.     IAP_CMD = CMD_READ;             //設置IAP命令
  238.     IAP_ADDRL = addr;               //設置IAP低地址
  239.     IAP_ADDRH = addr >> 8;          //設置IAP高地址
  240.     IAP_TRIG = 0x5a;                //寫觸發命令(0x5a)
  241.     IAP_TRIG = 0xa5;                //寫觸發命令(0xa5)
  242.         _nop_();                        //等待ISP/IAP/EEPROM操作完成
  243.     dat = IAP_DATA;                 //讀ISP/IAP/EEPROM數據
  244.     IapIdle();                      //關閉IAP功能

  245.     return dat;                     //返回
  246. }
  247. /*******************************************************************************
  248. * 文件名:void IapProgramByte(uint16 addr, uint8 dat)
  249. * 描  述: 寫一字節數據到ISP/IAP/EEPROM區域
  250. * 功  能:
  251. * 作  者:大核桃
  252. * 版本號:1.0.1(2017.05.23)
  253. *******************************************************************************/
  254. void IapProgramByte(uint16 addr, uint8 dat)
  255. {
  256.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  257.     IAP_CMD = CMD_PROGRAM;          //設置IAP命令
  258.     IAP_ADDRL = addr;               //設置IAP低地址
  259.     IAP_ADDRH = addr >> 8;          //設置IAP高地址
  260.     IAP_DATA = dat;                 //寫ISP/IAP/EEPROM數據
  261.     IAP_TRIG = 0x5a;                //寫觸發命令(0x5a)
  262.     IAP_TRIG = 0xa5;                //寫觸發命令(0xa5)
  263.         _nop_();                        //等待ISP/IAP/EEPROM操作完成
  264.     IapIdle();
  265. }

  266. /*******************************************************************************
  267. * 文件名:void IapEraseSector(uint16 addr)
  268. * 描  述: 扇區擦除
  269. * 功  能:
  270. * 作  者:大核桃
  271. * 版本號:1.0.1(2017.05.23)
  272. *******************************************************************************/
  273. void IapEraseSector(uint16 addr)
  274. {
  275.     IAP_CONTR = ENABLE_IAP;         //使能IAP
  276.     IAP_CMD = CMD_ERASE;            //設置IAP命令
  277.     IAP_ADDRL = addr;               //設置IAP低地址
  278.     IAP_ADDRH = addr >> 8;          //設置IAP高地址
  279.     IAP_TRIG = 0x5a;                //寫觸發命令(0x5a)
  280.     IAP_TRIG = 0xa5;                //寫觸發命令(0xa5)
  281.     _nop_();                        //等待ISP/IAP/EEPROM操作完成
  282.     IapIdle();
  283. }
復制代碼


程序上電后的執行效果圖片如下:可以看到程序記錄上電12次,稍后我們詳細的解析下這個程序。
000014.jpg

關于數碼管的一些問題
     一個8段的數碼管其實就是8個小燈啊,我們知道LED是有方向的,只有加正向偏置電壓才會點亮,正極的一端是陽極,負極的一端是陰極,如果我們把所有的陽極連到一個公共點,通過給其陰極一個低電位的方法能夠點亮的,叫做共陽極數碼管,那么共陰極數碼管就是倒過來了,高電平點亮,所有的陰極連在一起,限流電阻是友情提供的,實際是沒有的,如下圖所示:
000015.jpg 000016.jpg
有人可能覺得,那這8個小燈是如何排列的?怎么看呢?客官,您別急,我來畫一下,您就明白了。如下圖所示,共陽極數碼管示意圖:
000017.jpg
有了這張圖,我們來看一下程序,就好辦了,想一想,如果我要在數碼管上顯示一個數字0怎么弄呢?如果是共陽極數碼管。我應該讓ABCDEF都是0才可以,也即是說,點亮該段即可實現,那么結合我們前面所講解的數字電路知識,最高位我們不管,默認1即可 就是說要顯示一個0,那么八段從低到高依次是,a = 0,b = 0,c = 0,d = 0,e = 0,f = 0,g = 1,dot = 1;也就是二進制的1100_0000,16進制是0XC0,如果我們想要0-9這10個數字,那么是不是可以用同樣的方式,算出來,好了,真值表就是這么來的,至于共陽極,取反一下就是了。我們新建一個無符號字符型數組,將我們算好的數據放進數組里面。
  1. /*******************************************************************************
  2. * 文件名:共陽數碼管真值表
  3. * 描  述:
  4. * 功  能:
  5. * 作  者:大核桃
  6. * 版本號:1.0.1(2018.09.21)
  7. *******************************************************************************/
  8. code uint8 LedChar[] = {
  9.         0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
  10.         0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
  11. };
復制代碼


前面為什么要加一個CODE關鍵字呢?51單片機有好多關鍵字,默認都是藍色標識,注意,這個表示這個關鍵字在單片機系統中已經有名字了,不能隨便命名,CODE關鍵字的意思是將該部分代碼放在FLASH里面,而不是放在RAM里面,節省了程序運行空間,放在FLASH里面的變量是不能在程序運行時改變的。


關于數碼管的掃描刷新
我們了解一個常識,就是人的眼睛是不能夠分辨刷新速度小于10MS的物體的,就算變化了,你也看不出來的,最好的例子,就是,拿手機拍電視錄像,一條條的,就是因為手機拍攝的速度太快,而電視畫面刷新的太慢造成的,而這樣的現象,我們是看不見的。
用數碼管來顯示數字,基本上都是動態掃描刷新,所謂動態掃描,也就是先在1數碼管賦值,然后切換到2數碼管,切換到3,來回切換,我們只要把刷新速度控制在10MS之內,那么人的眼睛也看不出來的,我們這個代碼就是這樣進行處理的,如下所示;
  1. /*******************************************************************************
  2. * 文件名:void LedScan()
  3. * 描  述: LED刷新
  4. * 功  能:
  5. * 作  者:大核桃
  6. * 版本號:1.0.1(2017.05.23)
  7. *******************************************************************************/
  8. void LedScan()
  9. {
  10.         static uint8 i = 0;

  11.         P2 = 0Xff;
  12.         switch(i)
  13.         {
  14.                 case 0: LEDS4 = 0;LEDS1 = 1;P2 = LedChar[counter / 1000 % 10];i++;break;
  15.                 case 1: LEDS1 = 0;LEDS2 = 1;P2 = LedChar[counter / 100 % 10];i++;break;
  16.                 case 2: LEDS2 = 0;LEDS3 = 1;P2 = LedChar[counter / 10 % 10];i++;break;
  17.                 case 3: LEDS3 = 0;LEDS4 = 1;P2 = LedChar[counter % 10];i = 0;break;

  18.                 default:break;
  19.         }
  20. }
復制代碼

我們用到了SWITCH語句,SWITCH是一條多選一語句,以CASE為分支,break語句作為結束。我們來看下開發的原理圖,4個數碼管分別是NLED0,NLED1,NLED2,NLED3,這個段碼和位碼是如何選擇的呢?用萬用表的二極管檔位,我們知道二極管是單向導電的,我們又知道正向偏置是可以點亮小燈的,不斷的變換萬用表的表筆,將亮的段位和引腳記下來,按照提供的數碼管引腳圖就可以分出段碼和位碼來。
000018.jpg
如果我們要顯示一個1,打開對應的IO,那么我們只要對P2賦值P2 = LedChar[1]就好了;可是在實際應用中,我們需要顯示的更加復雜,因此,只能這樣動態進行賦值了,新建一個counter變量,然后將最低位的數碼管顯示個位,第二個數碼管顯示10位,第三個數碼管顯示百位,第四個數碼管顯示千位,依次這樣,相除取余數即可實現。


關于內部EEPROM
這個代碼,是從STC的客戶端上復制下來的,稍微整理了一下,不需要深入學習,你只要知道有多少個扇區,每個扇區的起始地址,就可以了,必要時候,回來翻閱數據手冊就可以搞定,使用的時候,一定要注意,同一扇區的數據會全部被擦除掉,如果不想全部擦除,一定要寫到不同的扇區,我們實現的功能是,先上電讀取一次0X0000地址的數據,然后我們counter++,然后我們擦除0X0000地址的數據,在重新向0X0000地址寫入一個新的數據就OK,注意,寫入之前先擦除,不然寫不進去的


今天的,就到這里吧,代碼獻上
006 內部EEPROM使用.rar (47.48 KB, 下載次數: 141)








回復

使用道具 舉報

ID:410020 發表于 2018-10-15 16:08 | 顯示全部樓層
不錯,不錯
回復

使用道具 舉報

ID:412788 發表于 2018-10-20 16:59 來自觸屏版 | 顯示全部樓層
非常有用 多謝樓主
回復

使用道具 舉報

ID:423217 發表于 2018-11-8 20:36 | 顯示全部樓層
厲害了,我的哥,贊一個
回復

使用道具 舉報

ID:430060 發表于 2018-11-20 17:57 | 顯示全部樓層
厲害了,我的哥,贊一個
回復

使用道具 舉報

ID:76133 發表于 2018-12-24 16:07 | 顯示全部樓層
多謝樓主贊一個
回復

使用道具 舉報

ID:378965 發表于 2019-1-14 11:14 | 顯示全部樓層
共用一個flash 長期測試是否穩定呢?
回復

使用道具 舉報

ID:378965 發表于 2019-1-14 11:15 | 顯示全部樓層
畢竟共用一個flash 長期測試是否穩定?
回復

使用道具 舉報

ID:461889 發表于 2019-1-23 14:51 | 顯示全部樓層
多謝樓主
回復

使用道具 舉報

ID:412814 發表于 2019-2-1 20:25 | 顯示全部樓層
謝謝樓主,學習了。
回復

使用道具 舉報

ID:472242 發表于 2019-3-5 00:01 | 顯示全部樓層
好案例,值得推薦!
回復

使用道具 舉報

ID:514110 發表于 2019-4-16 22:08 來自觸屏版 | 顯示全部樓層
謝謝,有用
回復

使用道具 舉報

ID:495990 發表于 2019-4-20 08:06 | 顯示全部樓層
贊一個
回復

使用道具 舉報

ID:248392 發表于 2019-5-10 11:50 | 顯示全部樓層
非常有用 多謝樓主
回復

使用道具 舉報

ID:482511 發表于 2019-6-11 07:08 來自觸屏版 | 顯示全部樓層
謝謝分享,但共用flash穩定嗎?
回復

使用道具 舉報

ID:495792 發表于 2019-6-11 08:58 | 顯示全部樓層
贊一個
回復

使用道具 舉報

ID:399179 發表于 2019-11-22 22:04 來自觸屏版 | 顯示全部樓層
感謝樓主分享!
回復

使用道具 舉報

ID:64931 發表于 2020-3-12 11:11 | 顯示全部樓層
多謝分享,正在找這方面資料
回復

使用道具 舉報

ID:399179 發表于 2021-1-30 21:09 來自觸屏版 | 顯示全部樓層
正在學習,謝謝樓主分享!
回復

使用道具 舉報

ID:976070 發表于 2022-2-4 22:13 | 顯示全部樓層
相當實用。正在找這方面資料。
回復

使用道具 舉報

ID:1021611 發表于 2022-4-25 17:35 | 顯示全部樓層
相當不錯,很實用的資料
回復

使用道具 舉報

ID:1049825 發表于 2022-10-31 20:23 | 顯示全部樓層
對論壇和樓主提供的幫助感謝萬分,有種茅塞頓開的感覺,謝了!
回復

使用道具 舉報

ID:1051419 發表于 2022-11-16 22:40 | 顯示全部樓層
counter=IapReadByte(0X0000);
counter++;這兩個怎么會報警,那里出錯
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 在线一区二区三区 | 久久久高清 | 欧美一级二级视频 | 一级片在线观看视频 | 91精品国产综合久久婷婷香蕉 | 久久久久国产精品一区二区 | 亚洲国产aⅴ精品 | 国产污视频在线 | 国产激情99| 九九热精品免费 | 久草新在线 | 九九免费观看视频 | 免费三级av | chengrenzaixian| 羞羞视频网站在线观看 | 天天插天天操 | 天天爱av | 在线亚州 | 欧美中文一区 | 国产乱码精品一品二品 | 草久久久 | 亚洲精品一区二区三区蜜桃久 | 国产电影一区 | 99热这里| 精品视频一区二区三区四区 | 亚洲永久 | 日韩电影一区二区三区 | 日产精品久久久一区二区福利 | 久久久久久久亚洲精品 | 性欧美精品一区二区三区在线播放 | 人人叉| 国产精品欧美一区二区三区不卡 | 中文字幕乱码一区二区三区 | 亚洲一区黄色 | 北条麻妃一区二区三区在线观看 | 日韩精品在线视频 | 毛片免费看 | 福利视频亚洲 | 午夜免费网站 | 日韩成人一区二区 | 久久福利电影 |