STC15W1K16PWM內部EEPROM的使用--20181013
在實際開發中,經常會遇到某些需要斷電需要再次上電記憶的場合,這就需要掉電存儲芯片了,最常用的EEPROM芯片就是AT24C02了,幾乎成了每一塊開發板的標配,但是有些時候,在一些低成本的場合,需要用類EEPROM或者flash來模擬EEPROM進行存儲,AT24C02是可以進行字節擦寫的,STC內部的EEPROM是不可以字節擦寫的,他是按照512byte一個扇區來組織的,如下圖所示,我們開發板選擇的這塊芯片分為了22個扇區。如果你要擦除數據,那么必須要一次性的擦除512字節才可以,這么難用,難用總比沒有強吧,在好多產品上,我都見到過,好多掉電了上電依然保持的參數,有沒有外置EEPROM芯片,只能用內部的或者來模擬了,我們來寫一個程序,程序的結果是記憶上電次數,每上電一次,就累加一次,顯示在數碼管上面,OK,寫好的代碼如下所示:
- /*******************************************************************************
- * 文件名: 數碼管顯示上電計數值
- * 描 述: 上電計數
- * 功 能:數碼管的使用
- * 作 者:大核桃 597627977
- * 版本號:1.0.1(2018.09.21)
- *******************************************************************************/
- #include "stc15w.h"//頭文件
- #include "intrins.h"
- /*******************************************************************************
- * 文件名: 重定義
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2018.09.21)
- *******************************************************************************/
- typedef unsigned char uint8;
- typedef unsigned int uint16;
- typedef unsigned long uint32;
- /*******************************************************************************
- * 文件名:共陽數碼管真值表
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2018.09.21)
- *******************************************************************************/
- code uint8 LedChar[] = {
- 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
- 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
- };
- uint16 counter; //記憶上電次數,最大65535
- /*******************************************************************************
- * 文件名:單獨位定義
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2018.09.21)
- *******************************************************************************/
- sbit LED0 = P1^0;//第1組LED
- sbit LED1 = P1^1;//第2組LED
- sbit LED2 = P1^2;//第3組LED
- sbit LED3 = P1^3;//第4組LED
- sbit LED4 = P1^4;//第5組LED
- sbit LED5 = P3^2;//第6組LED
- sbit LED6 = P0^0;//第7組LED
- sbit LED7 = P0^1;//第8組LED
- sbit LEDS1 = P3^3;//數碼管1
- sbit LEDS2 = P3^4;//數碼管2
- sbit LEDS3 = P3^6;//數碼管3
- sbit LEDS4 = P3^7;//數碼管4
- /*******************************************************************************
- * 文件名:全局變量定義區域
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- #define CMD_IDLE 0 //空閑模式
- #define CMD_READ 1 //IAP字節讀命令
- #define CMD_PROGRAM 2 //IAP字節編程命令
- #define CMD_ERASE 3 //IAP扇區擦除命令
- #define ENABLE_IAP 0x82 //if SYSCLK<20MHz
- /*******************************************************************************
- * 文件名:函數前置聲明
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Mcu_Port_Init();
- void LedScan();
- void Delay500ms(); //24MHZ
- void Time0_Init();//定時器0
- void IapIdle();
- uint8 IapReadByte(uint16 addr);
- void IapProgramByte(uint16 addr, uint8 dat);
- void IapEraseSector(uint16 addr);
- #define Delay() {_nop_();_nop_();_nop_();_nop_();}
- /*******************************************************************************
- * 文件名
- * 描 述: 主函數
- * 功 能:入口
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void main(void)
- {
- counter = IapReadByte(0x0000);//讀取數據
- counter++;//寫
- Mcu_Port_Init();//IO上電初始化
- Time0_Init();
- IapEraseSector(0x0000);//擦除數據
- IapProgramByte(0x0000, counter);//寫入數據
- while(1);
- }
- /*******************************************************************************
- * 文件名:void LedScan()
- * 描 述: LED刷新
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void LedScan()
- {
- static uint8 i = 0;
- P2 = 0Xff;
- switch(i)
- {
- case 0: LEDS4 = 0;LEDS1 = 1;P2 = LedChar[counter / 1000 % 10];i++;break;
- case 1: LEDS1 = 0;LEDS2 = 1;P2 = LedChar[counter / 100 % 10];i++;break;
- case 2: LEDS2 = 0;LEDS3 = 1;P2 = LedChar[counter / 10 % 10];i++;break;
- case 3: LEDS3 = 0;LEDS4 = 1;P2 = LedChar[counter % 10];i = 0;break;
- default:break;
- }
- }
- /*******************************************************************************
- * 文件名:void Time0_Init()
- * 描 述: 定時器0初始化
- * 功 能:10毫秒@11.0592MHz
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Time0_Init(void)
- {
- AUXR &= 0x7F; //定時器時鐘12T模式
- TMOD &= 0xF0; //設置定時器模式
- TMOD |= 0X01; //確保不干擾其他配置
- TH0 = 0xDC; //設置定時初值
- TL0 = 0x00; //設置定時初值
- ET0 = 1;
- TR0 = 1; //定時器0開始計時
- EA = 1;
- }
- /*******************************************************************************
- * 文件名:
- * 描 述: 中斷函數
- * 功 能:10毫秒@11.0592MHz
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void ET0_IRQHandler() interrupt 1
- {
- TH0 = 0xDC; //設置定時初值
- TL0 = 0x00; //設置定時初值
- LedScan();
- }
- /*******************************************************************************
- * 文件名:void Mcu_Port_Init()
- * 描 述: io初始化
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Mcu_Port_Init()
- {
- //將P0口低二位配置為推挽輸出
- //234567位配置位高阻輸入
- P0M1 = 0xFC;//1111 1100
- P0M0 = 0X03;//0000 0011
- //P0 = 0X01;//第6個
- //P0 = 0X02;//第7個
- //高3位配置高阻輸入,用作模擬口
- //其他配置推挽輸出,驅動LED
- P1M1 = 0xE0;//1110 0000
- P1M0 = 0X1F;//0001 1111
- //P2口配置準雙向口
- P2M1 = 0X00;
- P2M0 = 0X00;
- P2 = 0Xff; //上電為1111 1111
- // //P54,P55口為推挽輸出
- P5M1 = 0X00;
- P5M0 = 0X00;
- P5 = 0xFF;
- //P37,P36,3.2,P3.3 P3.4口為推挽輸出
- P3M1 = 0X00;
- P3M0 = 0XFC;
- P3 = 0X23; //0010 0111//第5個LED端口
-
- LED0 = 0;//第1組LED,如果使能請置為1
- LED1 = 0;
- LED2 = 0;
- LED3 = 0;
- LED4 = 0;
- LED5 = 0;
- LED6 = 0;
- LED7 = 0;
- }
- /*******************************************************************************
- * 文件名:void Delay500ms() //@24.000MHz
- * 描 述:Y5內核延時
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void Delay500ms() //@24.000MHz
- {
- unsigned char i, j, k;
- _nop_();
- _nop_();
- i = 46;
- j = 153;
- k = 245;
- do
- {
- do
- {
- while (--k);
- } while (--j);
- } while (--i);
- }
- /*******************************************************************************
- * 文件名:void IapIdle()
- * 描 述:關閉IAP
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void IapIdle()
- {
- IAP_CONTR = 0; //關閉IAP功能
- IAP_CMD = 0; //清除命令寄存器
- IAP_TRIG = 0; //清除觸發寄存器
- IAP_ADDRH = 0x80; //將地址設置到非IAP區域
- IAP_ADDRL = 0;
- }
- /*******************************************************************************
- * 文件名:uint8 IapReadByte(uint16 addr)
- * 描 述:從ISP/IAP/EEPROM區域讀取一字節
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- uint8 IapReadByte(uint16 addr)
- {
- uint8 dat; //數據緩沖區
- IAP_CONTR = ENABLE_IAP; //使能IAP
- IAP_CMD = CMD_READ; //設置IAP命令
- IAP_ADDRL = addr; //設置IAP低地址
- IAP_ADDRH = addr >> 8; //設置IAP高地址
- IAP_TRIG = 0x5a; //寫觸發命令(0x5a)
- IAP_TRIG = 0xa5; //寫觸發命令(0xa5)
- _nop_(); //等待ISP/IAP/EEPROM操作完成
- dat = IAP_DATA; //讀ISP/IAP/EEPROM數據
- IapIdle(); //關閉IAP功能
- return dat; //返回
- }
- /*******************************************************************************
- * 文件名:void IapProgramByte(uint16 addr, uint8 dat)
- * 描 述: 寫一字節數據到ISP/IAP/EEPROM區域
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void IapProgramByte(uint16 addr, uint8 dat)
- {
- IAP_CONTR = ENABLE_IAP; //使能IAP
- IAP_CMD = CMD_PROGRAM; //設置IAP命令
- IAP_ADDRL = addr; //設置IAP低地址
- IAP_ADDRH = addr >> 8; //設置IAP高地址
- IAP_DATA = dat; //寫ISP/IAP/EEPROM數據
- IAP_TRIG = 0x5a; //寫觸發命令(0x5a)
- IAP_TRIG = 0xa5; //寫觸發命令(0xa5)
- _nop_(); //等待ISP/IAP/EEPROM操作完成
- IapIdle();
- }
- /*******************************************************************************
- * 文件名:void IapEraseSector(uint16 addr)
- * 描 述: 扇區擦除
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void IapEraseSector(uint16 addr)
- {
- IAP_CONTR = ENABLE_IAP; //使能IAP
- IAP_CMD = CMD_ERASE; //設置IAP命令
- IAP_ADDRL = addr; //設置IAP低地址
- IAP_ADDRH = addr >> 8; //設置IAP高地址
- IAP_TRIG = 0x5a; //寫觸發命令(0x5a)
- IAP_TRIG = 0xa5; //寫觸發命令(0xa5)
- _nop_(); //等待ISP/IAP/EEPROM操作完成
- IapIdle();
- }
復制代碼
程序上電后的執行效果圖片如下:可以看到程序記錄上電12次,稍后我們詳細的解析下這個程序。
關于數碼管的一些問題
一個8段的數碼管其實就是8個小燈啊,我們知道LED是有方向的,只有加正向偏置電壓才會點亮,正極的一端是陽極,負極的一端是陰極,如果我們把所有的陽極連到一個公共點,通過給其陰極一個低電位的方法能夠點亮的,叫做共陽極數碼管,那么共陰極數碼管就是倒過來了,高電平點亮,所有的陰極連在一起,限流電阻是友情提供的,實際是沒有的,如下圖所示:
有人可能覺得,那這8個小燈是如何排列的?怎么看呢?客官,您別急,我來畫一下,您就明白了。如下圖所示,共陽極數碼管示意圖:
有了這張圖,我們來看一下程序,就好辦了,想一想,如果我要在數碼管上顯示一個數字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.0.1(2018.09.21)
- *******************************************************************************/
- code uint8 LedChar[] = {
- 0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
- 0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e
- };
復制代碼
前面為什么要加一個CODE關鍵字呢?51單片機有好多關鍵字,默認都是藍色標識,注意,這個表示這個關鍵字在單片機系統中已經有名字了,不能隨便命名,CODE關鍵字的意思是將該部分代碼放在FLASH里面,而不是放在RAM里面,節省了程序運行空間,放在FLASH里面的變量是不能在程序運行時改變的。
關于數碼管的掃描刷新
我們了解一個常識,就是人的眼睛是不能夠分辨刷新速度小于10MS的物體的,就算變化了,你也看不出來的,最好的例子,就是,拿手機拍電視錄像,一條條的,就是因為手機拍攝的速度太快,而電視畫面刷新的太慢造成的,而這樣的現象,我們是看不見的。
用數碼管來顯示數字,基本上都是動態掃描刷新,所謂動態掃描,也就是先在1數碼管賦值,然后切換到2數碼管,切換到3,來回切換,我們只要把刷新速度控制在10MS之內,那么人的眼睛也看不出來的,我們這個代碼就是這樣進行處理的,如下所示;
- /*******************************************************************************
- * 文件名:void LedScan()
- * 描 述: LED刷新
- * 功 能:
- * 作 者:大核桃
- * 版本號:1.0.1(2017.05.23)
- *******************************************************************************/
- void LedScan()
- {
- static uint8 i = 0;
- P2 = 0Xff;
- switch(i)
- {
- case 0: LEDS4 = 0;LEDS1 = 1;P2 = LedChar[counter / 1000 % 10];i++;break;
- case 1: LEDS1 = 0;LEDS2 = 1;P2 = LedChar[counter / 100 % 10];i++;break;
- case 2: LEDS2 = 0;LEDS3 = 1;P2 = LedChar[counter / 10 % 10];i++;break;
- case 3: LEDS3 = 0;LEDS4 = 1;P2 = LedChar[counter % 10];i = 0;break;
- default:break;
- }
- }
復制代碼
我們用到了SWITCH語句,SWITCH是一條多選一語句,以CASE為分支,break語句作為結束。我們來看下開發的原理圖,4個數碼管分別是NLED0,NLED1,NLED2,NLED3,這個段碼和位碼是如何選擇的呢?用萬用表的二極管檔位,我們知道二極管是單向導電的,我們又知道正向偏置是可以點亮小燈的,不斷的變換萬用表的表筆,將亮的段位和引腳記下來,按照提供的數碼管引腳圖就可以分出段碼和位碼來。
如果我們要顯示一個1,打開對應的IO,那么我們只要對P2賦值P2 = LedChar[1]就好了;可是在實際應用中,我們需要顯示的更加復雜,因此,只能這樣動態進行賦值了,新建一個counter變量,然后將最低位的數碼管顯示個位,第二個數碼管顯示10位,第三個數碼管顯示百位,第四個數碼管顯示千位,依次這樣,相除取余數即可實現。
關于內部EEPROM
這個代碼,是從STC的客戶端上復制下來的,稍微整理了一下,不需要深入學習,你只要知道有多少個扇區,每個扇區的起始地址,就可以了,必要時候,回來翻閱數據手冊就可以搞定,使用的時候,一定要注意,同一扇區的數據會全部被擦除掉,如果不想全部擦除,一定要寫到不同的扇區,我們實現的功能是,先上電讀取一次0X0000地址的數據,然后我們counter++,然后我們擦除0X0000地址的數據,在重新向0X0000地址寫入一個新的數據就OK,注意,寫入之前先擦除,不然寫不進去的。
今天的,就到這里吧,代碼獻上
006 內部EEPROM使用.rar
(47.48 KB, 下載次數: 141)
2018-10-13 08:55 上傳
點擊文件名下載附件
|