|
STC15W1K16PWM內(nèi)部EEPROM的使用--20181013
在實(shí)際開(kāi)發(fā)中,經(jīng)常會(huì)遇到某些需要斷電需要再次上電記憶的場(chǎng)合,這就需要掉電存儲(chǔ)芯片了,最常用的EEPROM芯片就是AT24C02了,幾乎成了每一塊開(kāi)發(fā)板的標(biāo)配,但是有些時(shí)候,在一些低成本的場(chǎng)合,需要用類EEPROM或者flash來(lái)模擬EEPROM進(jìn)行存儲(chǔ),AT24C02是可以進(jìn)行字節(jié)擦寫(xiě)的,STC內(nèi)部的EEPROM是不可以字節(jié)擦寫(xiě)的,他是按照512byte一個(gè)扇區(qū)來(lái)組織的,如下圖所示,我們開(kāi)發(fā)板選擇的這塊芯片分為了22個(gè)扇區(qū)。如果你要擦除數(shù)據(jù),那么必須要一次性的擦除512字節(jié)才可以,這么難用,難用總比沒(méi)有強(qiáng)吧,在好多產(chǎn)品上,我都見(jiàn)到過(guò),好多掉電了上電依然保持的參數(shù),有沒(méi)有外置EEPROM芯片,只能用內(nèi)部的或者來(lái)模擬了,我們來(lái)寫(xiě)一個(gè)程序,程序的結(jié)果是記憶上電次數(shù),每上電一次,就累加一次,顯示在數(shù)碼管上面,OK,寫(xiě)好的代碼如下所示:
000013.jpg (144.94 KB, 下載次數(shù): 131)
下載附件
2018-10-13 08:52 上傳
- /*******************************************************************************
- * 文件名: 數(shù)碼管顯示上電計(jì)數(shù)值
- * 描 述: 上電計(jì)數(shù)
- * 功 能:數(shù)碼管的使用
- * 作 者:大核桃 597627977
- * 版本號(hào):1.0.1(2018.09.21)
- *******************************************************************************/
- #include "stc15w.h"//頭文件
- #include "intrins.h"
- /*******************************************************************************
- * 文件名: 重定義
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2018.09.21)
- *******************************************************************************/
- typedef unsigned char uint8;
- typedef unsigned int uint16;
- typedef unsigned long uint32;
- /*******************************************************************************
- * 文件名:共陽(yáng)數(shù)碼管真值表
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):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; //記憶上電次數(shù),最大65535
- /*******************************************************************************
- * 文件名:單獨(dú)位定義
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):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;//數(shù)碼管1
- sbit LEDS2 = P3^4;//數(shù)碼管2
- sbit LEDS3 = P3^6;//數(shù)碼管3
- sbit LEDS4 = P3^7;//數(shù)碼管4
- /*******************************************************************************
- * 文件名:全局變量定義區(qū)域
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- #define CMD_IDLE 0 //空閑模式
- #define CMD_READ 1 //IAP字節(jié)讀命令
- #define CMD_PROGRAM 2 //IAP字節(jié)編程命令
- #define CMD_ERASE 3 //IAP扇區(qū)擦除命令
- #define ENABLE_IAP 0x82 //if SYSCLK<20MHz
- /*******************************************************************************
- * 文件名:函數(shù)前置聲明
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void Mcu_Port_Init();
- void LedScan();
- void Delay500ms(); //24MHZ
- void Time0_Init();//定時(shí)器0
- void IapIdle();
- uint8 IapReadByte(uint16 addr);
- void IapProgramByte(uint16 addr, uint8 dat);
- void IapEraseSector(uint16 addr);
- #define Delay() {_nop_();_nop_();_nop_();_nop_();}
- /*******************************************************************************
- * 文件名
- * 描 述: 主函數(shù)
- * 功 能:入口
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void main(void)
- {
- counter = IapReadByte(0x0000);//讀取數(shù)據(jù)
- counter++;//寫(xiě)
- Mcu_Port_Init();//IO上電初始化
- Time0_Init();
- IapEraseSector(0x0000);//擦除數(shù)據(jù)
- IapProgramByte(0x0000, counter);//寫(xiě)入數(shù)據(jù)
- while(1);
- }
- /*******************************************************************************
- * 文件名:void LedScan()
- * 描 述: LED刷新
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):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()
- * 描 述: 定時(shí)器0初始化
- * 功 能:10毫秒@11.0592MHz
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void Time0_Init(void)
- {
- AUXR &= 0x7F; //定時(shí)器時(shí)鐘12T模式
- TMOD &= 0xF0; //設(shè)置定時(shí)器模式
- TMOD |= 0X01; //確保不干擾其他配置
- TH0 = 0xDC; //設(shè)置定時(shí)初值
- TL0 = 0x00; //設(shè)置定時(shí)初值
- ET0 = 1;
- TR0 = 1; //定時(shí)器0開(kāi)始計(jì)時(shí)
- EA = 1;
- }
- /*******************************************************************************
- * 文件名:
- * 描 述: 中斷函數(shù)
- * 功 能:10毫秒@11.0592MHz
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void ET0_IRQHandler() interrupt 1
- {
- TH0 = 0xDC; //設(shè)置定時(shí)初值
- TL0 = 0x00; //設(shè)置定時(shí)初值
- LedScan();
- }
- /*******************************************************************************
- * 文件名:void Mcu_Port_Init()
- * 描 述: io初始化
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void Mcu_Port_Init()
- {
- //將P0口低二位配置為推挽輸出
- //234567位配置位高阻輸入
- P0M1 = 0xFC;//1111 1100
- P0M0 = 0X03;//0000 0011
- //P0 = 0X01;//第6個(gè)
- //P0 = 0X02;//第7個(gè)
- //高3位配置高阻輸入,用作模擬口
- //其他配置推挽輸出,驅(qū)動(dòng)LED
- P1M1 = 0xE0;//1110 0000
- P1M0 = 0X1F;//0001 1111
- //P2口配置準(zhǔn)雙向口
- 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個(gè)LED端口
-
- LED0 = 0;//第1組LED,如果使能請(qǐng)置為1
- LED1 = 0;
- LED2 = 0;
- LED3 = 0;
- LED4 = 0;
- LED5 = 0;
- LED6 = 0;
- LED7 = 0;
- }
- /*******************************************************************************
- * 文件名:void Delay500ms() //@24.000MHz
- * 描 述:Y5內(nèi)核延時(shí)
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):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()
- * 描 述:關(guān)閉IAP
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void IapIdle()
- {
- IAP_CONTR = 0; //關(guān)閉IAP功能
- IAP_CMD = 0; //清除命令寄存器
- IAP_TRIG = 0; //清除觸發(fā)寄存器
- IAP_ADDRH = 0x80; //將地址設(shè)置到非IAP區(qū)域
- IAP_ADDRL = 0;
- }
- /*******************************************************************************
- * 文件名:uint8 IapReadByte(uint16 addr)
- * 描 述:從ISP/IAP/EEPROM區(qū)域讀取一字節(jié)
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- uint8 IapReadByte(uint16 addr)
- {
- uint8 dat; //數(shù)據(jù)緩沖區(qū)
- IAP_CONTR = ENABLE_IAP; //使能IAP
- IAP_CMD = CMD_READ; //設(shè)置IAP命令
- IAP_ADDRL = addr; //設(shè)置IAP低地址
- IAP_ADDRH = addr >> 8; //設(shè)置IAP高地址
- IAP_TRIG = 0x5a; //寫(xiě)觸發(fā)命令(0x5a)
- IAP_TRIG = 0xa5; //寫(xiě)觸發(fā)命令(0xa5)
- _nop_(); //等待ISP/IAP/EEPROM操作完成
- dat = IAP_DATA; //讀ISP/IAP/EEPROM數(shù)據(jù)
- IapIdle(); //關(guān)閉IAP功能
- return dat; //返回
- }
- /*******************************************************************************
- * 文件名:void IapProgramByte(uint16 addr, uint8 dat)
- * 描 述: 寫(xiě)一字節(jié)數(shù)據(jù)到ISP/IAP/EEPROM區(qū)域
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void IapProgramByte(uint16 addr, uint8 dat)
- {
- IAP_CONTR = ENABLE_IAP; //使能IAP
- IAP_CMD = CMD_PROGRAM; //設(shè)置IAP命令
- IAP_ADDRL = addr; //設(shè)置IAP低地址
- IAP_ADDRH = addr >> 8; //設(shè)置IAP高地址
- IAP_DATA = dat; //寫(xiě)ISP/IAP/EEPROM數(shù)據(jù)
- IAP_TRIG = 0x5a; //寫(xiě)觸發(fā)命令(0x5a)
- IAP_TRIG = 0xa5; //寫(xiě)觸發(fā)命令(0xa5)
- _nop_(); //等待ISP/IAP/EEPROM操作完成
- IapIdle();
- }
- /*******************************************************************************
- * 文件名:void IapEraseSector(uint16 addr)
- * 描 述: 扇區(qū)擦除
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):1.0.1(2017.05.23)
- *******************************************************************************/
- void IapEraseSector(uint16 addr)
- {
- IAP_CONTR = ENABLE_IAP; //使能IAP
- IAP_CMD = CMD_ERASE; //設(shè)置IAP命令
- IAP_ADDRL = addr; //設(shè)置IAP低地址
- IAP_ADDRH = addr >> 8; //設(shè)置IAP高地址
- IAP_TRIG = 0x5a; //寫(xiě)觸發(fā)命令(0x5a)
- IAP_TRIG = 0xa5; //寫(xiě)觸發(fā)命令(0xa5)
- _nop_(); //等待ISP/IAP/EEPROM操作完成
- IapIdle();
- }
復(fù)制代碼
程序上電后的執(zhí)行效果圖片如下:可以看到程序記錄上電12次,稍后我們?cè)敿?xì)的解析下這個(gè)程序。
000014.jpg (2.02 MB, 下載次數(shù): 140)
下載附件
2018-10-13 08:53 上傳
關(guān)于數(shù)碼管的一些問(wèn)題
一個(gè)8段的數(shù)碼管其實(shí)就是8個(gè)小燈啊,我們知道LED是有方向的,只有加正向偏置電壓才會(huì)點(diǎn)亮,正極的一端是陽(yáng)極,負(fù)極的一端是陰極,如果我們把所有的陽(yáng)極連到一個(gè)公共點(diǎn),通過(guò)給其陰極一個(gè)低電位的方法能夠點(diǎn)亮的,叫做共陽(yáng)極數(shù)碼管,那么共陰極數(shù)碼管就是倒過(guò)來(lái)了,高電平點(diǎn)亮,所有的陰極連在一起,限流電阻是友情提供的,實(shí)際是沒(méi)有的,如下圖所示:
000015.jpg (100.58 KB, 下載次數(shù): 138)
下載附件
2018-10-13 08:53 上傳
000016.jpg (108.68 KB, 下載次數(shù): 135)
下載附件
2018-10-13 08:53 上傳
有人可能覺(jué)得,那這8個(gè)小燈是如何排列的啊?怎么看呢?客官,您別急,我來(lái)畫(huà)一下,您就明白了。如下圖所示,共陽(yáng)極數(shù)碼管示意圖:
000017.jpg (205.61 KB, 下載次數(shù): 136)
下載附件
2018-10-13 08:53 上傳
有了這張圖,我們來(lái)看一下程序,就好辦了,想一想,如果我要在數(shù)碼管上顯示一個(gè)數(shù)字0怎么弄呢?如果是共陽(yáng)極數(shù)碼管。我應(yīng)該讓ABCDEF都是0才可以,也即是說(shuō),點(diǎn)亮該段即可實(shí)現(xiàn),那么結(jié)合我們前面所講解的數(shù)字電路知識(shí),最高位我們不管,默認(rèn)1即可 就是說(shuō)要顯示一個(gè)0,那么八段從低到高依次是,a = 0,b = 0,c = 0,d = 0,e = 0,f = 0,g = 1,dot = 1;也就是二進(jìn)制的1100_0000,16進(jìn)制是0XC0,如果我們想要0-9這10個(gè)數(shù)字,那么是不是可以用同樣的方式,算出來(lái),好了,真值表就是這么來(lái)的,至于共陽(yáng)極,取反一下就是了。我們新建一個(gè)無(wú)符號(hào)字符型數(shù)組,將我們算好的數(shù)據(jù)放進(jìn)數(shù)組里面。
- /*******************************************************************************
- * 文件名:共陽(yáng)數(shù)碼管真值表
- * 描 述:
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):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
- };
復(fù)制代碼
前面為什么要加一個(gè)CODE關(guān)鍵字呢?51單片機(jī)有好多關(guān)鍵字,默認(rèn)都是藍(lán)色標(biāo)識(shí),注意,這個(gè)表示這個(gè)關(guān)鍵字在單片機(jī)系統(tǒng)中已經(jīng)有名字了,不能隨便命名,CODE關(guān)鍵字的意思是將該部分代碼放在FLASH里面,而不是放在RAM里面,節(jié)省了程序運(yùn)行空間,放在FLASH里面的變量是不能在程序運(yùn)行時(shí)改變的。
關(guān)于數(shù)碼管的掃描刷新
我們了解一個(gè)常識(shí),就是人的眼睛是不能夠分辨刷新速度小于10MS的物體的,就算變化了,你也看不出來(lái)的,最好的例子,就是,拿手機(jī)拍電視錄像,一條條的,就是因?yàn)槭謾C(jī)拍攝的速度太快,而電視畫(huà)面刷新的太慢造成的,而這樣的現(xiàn)象,我們是看不見(jiàn)的。
用數(shù)碼管來(lái)顯示數(shù)字,基本上都是動(dòng)態(tài)掃描刷新,所謂動(dòng)態(tài)掃描,也就是先在1數(shù)碼管賦值,然后切換到2數(shù)碼管,切換到3,來(lái)回切換,我們只要把刷新速度控制在10MS之內(nèi),那么人的眼睛也看不出來(lái)的,我們這個(gè)代碼就是這樣進(jìn)行處理的,如下所示;
- /*******************************************************************************
- * 文件名:void LedScan()
- * 描 述: LED刷新
- * 功 能:
- * 作 者:大核桃
- * 版本號(hào):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;
- }
- }
復(fù)制代碼
我們用到了SWITCH語(yǔ)句,SWITCH是一條多選一語(yǔ)句,以CASE為分支,break語(yǔ)句作為結(jié)束。我們來(lái)看下開(kāi)發(fā)的原理圖,4個(gè)數(shù)碼管分別是NLED0,NLED1,NLED2,NLED3,這個(gè)段碼和位碼是如何選擇的呢?用萬(wàn)用表的二極管檔位,我們知道二極管是單向?qū)щ姷模覀冇种勒蚱檬强梢渣c(diǎn)亮小燈的,不斷的變換萬(wàn)用表的表筆,將亮的段位和引腳記下來(lái),按照提供的數(shù)碼管引腳圖就可以分出段碼和位碼來(lái)。
000018.jpg (86.36 KB, 下載次數(shù): 141)
下載附件
2018-10-13 08:54 上傳
如果我們要顯示一個(gè)1,打開(kāi)對(duì)應(yīng)的IO,那么我們只要對(duì)P2賦值P2 = LedChar[1]就好了;可是在實(shí)際應(yīng)用中,我們需要顯示的更加復(fù)雜,因此,只能這樣動(dòng)態(tài)進(jìn)行賦值了,新建一個(gè)counter變量,然后將最低位的數(shù)碼管顯示個(gè)位,第二個(gè)數(shù)碼管顯示10位,第三個(gè)數(shù)碼管顯示百位,第四個(gè)數(shù)碼管顯示千位,依次這樣,相除取余數(shù)即可實(shí)現(xiàn)。
關(guān)于內(nèi)部EEPROM
這個(gè)代碼,是從STC的客戶端上復(fù)制下來(lái)的,稍微整理了一下,不需要深入學(xué)習(xí),你只要知道有多少個(gè)扇區(qū),每個(gè)扇區(qū)的起始地址,就可以了,必要時(shí)候,回來(lái)翻閱數(shù)據(jù)手冊(cè)就可以搞定,使用的時(shí)候,一定要注意,同一扇區(qū)的數(shù)據(jù)會(huì)全部被擦除掉,如果不想全部擦除,一定要寫(xiě)到不同的扇區(qū),我們實(shí)現(xiàn)的功能是,先上電讀取一次0X0000地址的數(shù)據(jù),然后我們counter++,然后我們擦除0X0000地址的數(shù)據(jù),在重新向0X0000地址寫(xiě)入一個(gè)新的數(shù)據(jù)就OK,注意,寫(xiě)入之前先擦除,不然寫(xiě)不進(jìn)去的。
今天的,就到這里吧,代碼獻(xiàn)上
006 內(nèi)部EEPROM使用.rar
(47.48 KB, 下載次數(shù): 141)
2018-10-13 08:55 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
|
|