设置 | 登录 | 注册

作者共发了8篇帖子。

【程序】24C08存儲器的操作

1楼 巨大八爪鱼 2016-7-19 11:46
#include <reg52.h>
#include <intrins.h>

#define LCDClear() LCDWriteCmd(0x01)

sbit RS = P2^0;
sbit RW = P2^1;
sbit E = P2^2;
sbit BF = P0^7;

sbit SCL = P3^4;
sbit SDA = P3^5;

void delay(unsigned int n)
{
    unsigned char i;
    while (n--)
        for (i = 0; i < 115; i++);
}

void LCDBusyTest(void)
{
    E = 0;
    RS = 0;
    RW = 1;
    E = 1;
    BF = 1;
    while (BF);
    E = 0;
}

void LCDWriteCmd(unsigned char cmd)
{
    LCDBusyTest();
    RS = 0;
    RW = 0;
    E = 1;
    P0 = cmd;
    E = 0;
}

void LCDWriteData(unsigned char dat)
{
    LCDBusyTest();
    RS = 1;
    RW = 0;
    E = 1;
    P0 = dat;
    E = 0;
}

void LCDWriteString(char *s)
{
    while (*s)
        LCDWriteData(*s++);
}

void LCDWriteNumber(unsigned char num)
{
    char str[4];
    str[0] = num / 100 + '0';
    str[1] = num % 100 / 10 + '0';
    str[2] = num % 10 + '0';
    str[3] = '\0';
    if (str[0] == '0')
    {
        if (str[1] == '0')
            LCDWriteString(str + 2);
        else
            LCDWriteString(str + 1);
    }
    else
        LCDWriteString(str);
}

void LCDInit(void)
{
    delay(40);
    LCDWriteCmd(0x30);
    delay(10);
    LCDWriteCmd(0x30);
    delay(10);
    LCDWriteCmd(0x0c);
    delay(1);
    LCDWriteCmd(0x01);
}

void I2CStart(void)
{
    // SCL為高電平時, SDA必須穩定
    // 所以必須先置位SDA, 後置位SCL, 順序絕不能顛倒過來
    SDA = 1;
    SCL = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    SDA = 0;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
}

void I2CStop(void)
{
    SDA = 0;
    SCL = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    SDA = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
}

// 檢查從器件的應答
bit I2CAck(void)
{
    bit ack;
    SDA = 1;
    SCL = 0;
    _nop_();
    _nop_();
    SCL = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    ack = SDA;
    SCL = 0;
    return !ack;
}

// 主器件主動應答
// 1為應答, 0為非應答
void I2CSendAck(bit ack)
{
    SDA = !ack;
    SCL = 0;
    _nop_();
    _nop_();
    SCL = 1;
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    _nop_();
    SCL = 0;
}

bit I2CWrite(unsigned char dat)
{
    unsigned char i;
    for (i = 0; i < 8; i++)
    {
        SCL = 0;
        SDA = dat & 0x80;
        dat <<= 1;
        SCL = 1;
        _nop_();
        _nop_();
    }
    SCL = 0;
    return I2CAck();
}

unsigned char I2CRead(void)
{
    unsigned char i;
    unsigned char dat = 0;
    SDA = 1;
    for (i = 0; i < 8; i++)
    {
        SCL = 1;
        dat <<= 1;
        if (SDA)
            dat |= 1;
        SCL = 0;
        _nop_();
        _nop_();
    }
    return dat;
}

// 跳轉到指定地址
bit EEPGoto(unsigned int addr, bit read)
{
    unsigned char cmd = 0xa0;
    cmd |= addr >> 7 & 0x06;
    if (!I2CWrite(cmd)) // slave address for WRITING
        return 0;
    if (!I2CWrite(addr & 0xff)) // byte address
        return 0;
    if (read)
    {
        I2CStart();
        return I2CWrite(cmd | read); // slave address again for reading
    }
    else
        return 1;
}

// 單字節寫
bit EEPWriteByte(unsigned int addr, unsigned char dat)
{
    bit result = 0;
    I2CStart();
    if (EEPGoto(addr, 0))
        result = I2CWrite(dat);
    I2CStop();
    if (result)
        delay(1); // 等待器件擦寫完畢
    return result;
}

// 頁寫
// 返回成功寫入的字節數
// 24C08中每頁為16位元組,一次只能寫一頁, 如果在0地址處寫入20個字符,那麼第17~20個字符將會被寫回0~3地址處
// 如果在2地址處寫入16個字符,那麼第15~16個字符就會被寫入到0~1地址處
unsigned char EEPWritePage(unsigned int addr, unsigned char dat[], unsigned char size)
{
    unsigned char n = 0;
    if (size == 0)
        return 0;
    
    I2CStart();
    if (EEPGoto(addr, 0))
    {
        while (n < size)
        {
            if (!I2CWrite(dat[n]))
                break;
            n++;
        }
    }
    I2CStop();
    if (n > 0)
        delay(1);
    return n;
}

// 選擇性讀
bit EEPReadByte(unsigned int addr, unsigned char *dat)
{
    bit result = 0;
    I2CStart();
    if (EEPGoto(addr, 1))
    {
        result = 1;
        *dat = I2CRead();
    }
    I2CSendAck(0);
    I2CStop();
    return result;
}

// 立即地址讀
// 讀到整個存儲器最後一個字節時會自動跳回0地址
bit EEPReadByteIn(unsigned char slave_addr, unsigned char *dat)
{
    bit result = 0;
    I2CStart();
    if (I2CWrite(slave_addr))
    {
        result = 1;
        *dat = I2CRead();
    }
    I2CSendAck(0); // NOACK
    I2CStop();
    return result;
}

// 由選擇性讀啟動連續讀
unsigned char EEPReadPage(unsigned int addr, unsigned char dat[], unsigned char size)
{
    unsigned char n = 0;
    if (size == 0)
        return 0;
    
    I2CStart();
    if (EEPGoto(addr, 1))
    {
        while (n < size)
        {
            if (n != 0)
                I2CSendAck(1); // 應答表示主器件要求更多的數據
            dat[n] = I2CRead();
            n++;
        }
    }
    I2CSendAck(0); // 非應答表示停止發送
    I2CStop();
    return n;
}

// 由立即地址讀啟動連續讀
unsigned char EEPReadPageIn(unsigned char slave_addr, unsigned char dat[], unsigned char size)
{
    unsigned char n = 0;
    if (size == 0)
        return 0;
    
    I2CStart();
    if (I2CWrite(slave_addr))
    {
        while (n < size)
        {
            if (n != 0)
                I2CSendAck(1);
            dat[n] = I2CRead();
            n++;
        }
    }
    I2CSendAck(0);
    I2CStop();
    return n;
}

void test(void)
{
    char buf[16];
    char ch;
    unsigned char i, n;
    unsigned char cnt = 0;
    
    /* 測試單字節寫 */
    // 24C08的地址範圍: 0x000~0x3ff
    cnt += (unsigned char)EEPWriteByte(0x3fd, 'K');
    cnt += (unsigned char)EEPWriteByte(0x3fe, 'e');
    cnt += (unsigned char)EEPWriteByte(0x3ff, 'y');
    
    /* 測試單字節讀 */
    LCDClear();
    cnt += (unsigned char)EEPReadByte(0x3fd, &ch);
    LCDWriteData(ch);
    cnt += (unsigned char)EEPReadByte(0x3fe, &ch);
    LCDWriteData(ch);
    cnt += (unsigned char)EEPReadByte(0x3ff, &ch);
    LCDWriteData(ch);
    
    /* 測試頁寫 */
    cnt += EEPWritePage(0x302, "簡體中文abcd1234繁", 18);
    // 由於一次只能寫一頁, 每頁16位元組
    // 所以最後的34被寫到了0x300~0x301處, "繁"字把"簡"字覆蓋了
    
    /* 測試立即地址連續讀 */
    // 當前地址位於"繁"字後面,也就是0x304處
    n = EEPReadPageIn(0xa7, buf, 8); // A2接的是低電平, 且要讀的區域為第三區域, 所以參數1的第3位為0, 第2~1位為11
    LCDWriteCmd(0x90); // 跳至第二行
    for (i = 0; i < n; i++)
        LCDWriteData(buf[i]);
    cnt += n;
    
    /* 測試立即地址讀 */
    // 當前地址位於"b"字後面, 也就是0x30c處
    LCDWriteData('-');
    cnt += (unsigned char)EEPReadByteIn(0xa7, &ch);
    LCDWriteData(ch);
    cnt += (unsigned char)EEPReadByteIn(0xa7, &ch);
    LCDWriteData(ch);
    
    /* 測試選擇性連續讀 */
    n = EEPReadPage(0x300, buf, 16);
    LCDWriteCmd(0x88); // 跳至第三行
    for (i = 0; i < n; i++)
        LCDWriteData(buf[i]);
    cnt += n;
    
    LCDWriteCmd(0x98); // 跳至第四行
    LCDWriteString("共成功操作了");
    LCDWriteNumber(cnt);
    LCDWriteString("B");
}

int main(void)
{
    LCDInit();
    test();
    while (1);
}
2楼 巨大八爪鱼 2016-7-19 11:52
3楼 巨大八爪鱼 2016-7-19 12:07
4楼 巨大八爪鱼 2016-7-19 12:08
本程序使用的液晶為12864液晶。
程序的運行結果如下:
5楼 巨大八爪鱼 2016-7-19 12:14
本程序所用的晶振為11.0592MHz,_nop_()函數延時大約為1μs,位於頭文件intrins.h中。
6楼 巨大八爪鱼 2016-7-19 13:13
【讀寫指定大小的數據塊的函數】
// 寫入數據塊, 返回寫入的字節數
unsigned int EEPWriteBlock(unsigned int addr, void *dat, unsigned int size)
{
    unsigned char n = 16 - addr & 0x0f; // 計算當前頁還剩下多少字節可寫
    unsigned char cur;
    unsigned char written = 0;
    if (addr >= 0x400)
        return 0;
    while (n > 0)
    {
        cur = EEPWritePage(addr, dat, n);
        written += cur;
        if (cur < n)
            break;
        addr += n;
        dat = (unsigned char *)dat + n;
        if (addr >= 0x400)
            addr -= 0x400; // 當到達器件末尾時不給出錯誤提示, 而是跳至0x00地址處繼續寫
       
        size -= n;
        n = 16; // 下一次要寫入的字節數
        if (size < n)
            n = size;
    }
    return written;
}

// 讀取數據塊, 返回寫入的字節數
// 由於24C08的連續讀時序可以一次性讀取整個晶片, 而不是僅僅局限於當前頁或區域
// 所以直接調用EEPReadPage函數就行了
#define EEPReadBlock(addr, buf, size) EEPReadPage(addr, (unsigned char *)(buf), size)
7楼 巨大八爪鱼 2016-7-19 13:15
【測試讀寫數據塊】
sbit K1 = P1^4;
sbit K2 = P1^5;

// 定義結構體
struct student
{
    char id[5];
    char name[20];
    unsigned char age;
    unsigned char score;
};

char *strcpy(char *s1, const char *s2); // 聲明庫函數中的strcpy函數
void writedata(void)
{
    struct student stu;
    unsigned char n;
    strcpy(stu.id, "SM49");
    strcpy(stu.name, "巨大八爪魚");
    stu.age = 14;
    stu.score = 78;
    n = EEPWriteBlock(0x2f8, &stu, sizeof(stu));
    
    LCDClear();
    LCDWriteString("寫入");
    LCDWriteNumber(n);
    LCDWriteString("字節");
    LCDWriteCmd(0x90);
    LCDWriteString("sizeof(stu)=");
    LCDWriteNumber(sizeof(stu));
}

void readdata(void)
{
    struct student stu;
    unsigned char n = EEPReadBlock(0x2f8, &stu, sizeof(stu));
    LCDClear();
    LCDWriteString("讀入");
    LCDWriteNumber(n);
    LCDWriteString("字節");
    LCDWriteCmd(0x90);
    LCDWriteString("ID:");
    LCDWriteString(stu.id);
    LCDWriteCmd(0x88);
    LCDWriteString("Name: ");
    LCDWriteString(stu.name);
    LCDWriteCmd(0x98);
    LCDWriteString("Age:");
    LCDWriteNumber(stu.age);
    LCDWriteString(" Score:");
    LCDWriteNumber(stu.score);
}

int main(void)
{
    LCDInit();
    test();
    while (1)
    {
        if (!K1)
        {
            delay(15);
            if (!K1)
            {
                writedata();
                while (!K1);
            }
        }
        if (!K2)
        {
            delay(15);
            if (!K2)
            {
                readdata();
                while (!K2);
            }
        }
    }
}
8楼 巨大八爪鱼 2016-7-19 13:19
【程序運行結果】


内容转换:

回复帖子
内容:
用户名: 您目前是匿名发表。
验证码:
看不清?换一张
©2010-2025 Purasbar Ver3.0 [手机版] [桌面版]
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。