目前共有8篇帖子。 內容轉換:不轉換▼
 
點擊 回復
778 7
【程序】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
【程序运行结果】


回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
(快捷鍵:Ctrl+Enter)
 

本帖信息

點擊數:778 回複數:7
評論數: ?
作者:巨大八爪鱼
最後回復:巨大八爪鱼
最後回復時間:2016-7-19 13:19
 
©2010-2024 Arslanbar Ver2.0
除非另有聲明,本站採用共享創意姓名標示-相同方式分享 3.0 Unported許可協議進行許可。