求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
单片机教程
第一章 单片机入门
1.单片机简介
2.开发板选择
3.前期准备
第二章 LED及入门
1.初识原理图
2.程序点灯
3.实际LED硬件连接
4.点亮LED
5.闪烁的LED
6.软件调试查看运行时间
7.延时1秒
8.函数封装
9.函数传参调用
10.流水灯
11.数组与移位
第三章 蜂鸣器
1.蜂鸣器原理
2.无源蜂鸣器鸣叫
3.无源蜂鸣器题目
4.无源蜂鸣器+LED
第四章 数码管
1.数码管原理
2.数组&数码管
3.数码管&LED
4.多个数码管显示
5.同时显示不同的数字
6.高位不显示0
7.数码管小数点
8.分钟秒表
第五章 独立按键
1.按键入门
2.按键&蜂鸣器&数码管
3.按键猜想
4.按键用法
5.按键模式
6.不支持连按
7.静态变量
8.支持连按与全局变量
9.再次优化不支持连按的代码
10.流水灯的优化解说
11. 代码对比
12.双模式函数封装
13.第一阶段综合例程(上)
14.第一阶段综合例程(下)
15.第一阶段的总结
第六章 多文件编程
1.多文件编写
2.模块初始化
3.数码管显示函数
4.多文件编程首次测试
5.带返回值的函数
6.新按键程序
7.最终按键程序
8.最终的function文件
第七章 外部中断
1.寄存器
2.中断函数
3.进一步理解中断函数
4.中断的实验现象
5.外部中断测脉冲个数
第八章 定时器
1.定时器概念
2.定时器工作模式
3.定时时长的做法
4.定时器简单运用
5.定时器工作模式2
6.定时器中断函数的使用
7.定时器初始化新写法
8.隐形漏洞
9.代码参考
10.微调定时精确时间
11.单独文件封装
12.输入捕获
13.数据类型强制转换
14.定时器&数码管扫描显示
15.呼吸灯
第九章 舵机与超声波模块
1.舵机入门
2.舵机与按键
3.超声波模块
第十章 串口通信
1.串口通信入门
2.简洁式串口通信
3.详细理解ASCII码
4.串口printf系列函数
第十一章 1602液晶屏
1.液晶屏代码讲解
2.指向数组的指针
3.液晶屏代码单独文件
4.各功能代码的运用
第十二章 IIC通信
1.IIC入门代码讲解
2.EEPROM简单使用
3.IIC&EEPROM合成文件
第十三章 红外遥控与温度传感器
1.红外遥控
2.代码文件和测试
3.温度传感器代码解析Ⅰ
4.温度传感器代码解析Ⅱ
5.温度传感器代码独立文件
第十四章 AD与DA
1.AD与DA合成文件
第十五章 混合例程
1.电位器控制舵机
2.遥控器控制舵机
3.温度传感器与串口
4.模拟倒车雷达报警
5.再次熟悉串口
6.串口&液晶屏Ⅰ
7.串口&液晶屏Ⅱ
8.串口&液晶屏Ⅲ
9.串口&EEPROM
10.实践操作
11.结束语
 
 
目录
EEPROM简单使用
来源:C语言网    作者:继承叔    编辑:Alice(火龙果软件)
317 次浏览
1次  

我们这一讲简要讲解如何往EEPROM的地址0x55写入一个数据,然后读出这个数据的内容。本讲代码围绕的是宋老师的lesson14_2例程代码讲解。

1.写入一个字节内容

如果要在EEPROM的某个地址里写入内容,那么步骤的实现归为:

起始信号→找到这个器件是否存在(寻址),发送的字节最低位要为0意为要往这个器件写内容→选择EEPROM的哪个地址去写→写入8位的数据→停止信号。

宋老师写的“void E2WriteByte(unsigned char addr, unsigned char dat)”函数里面,上一讲都讲解过里面的函数了,写入一个字节内容的讲解我们就介绍完了。

2.读出一个字节内容

在“unsigned char E2ReadByte(unsigned char addr)”中前面三个函数与“void E2WriteByte(unsigned char addr, unsigned char dat)”都是一样的操作步骤,选定好要读出哪个地址的内容,然后还需再重新发送起始信号,接着是把寻址的字节最低位设置为1意为要读出EEPROM的某个地址里面的内容,因为只读一个字节,所以单片机在接收完EEPROM发送回来的数据(这个数据就是当初写进去的数据)之后,不产生拉低应答(是单片机不产生应答,不是说EEPROM不产生应答),这样EEPROM就不会再发送数据回来了,达到了只读一个字节的功能。

所谓单片机读EEPROM的数据出来,其实就是EEPROM在SDA线上不停地拉高拉低变化,而单片机就是不断地判断第一位是0或者1,第二位是0或者1······,这个细节过程大家有能力的话可以一步步去解读宋老师写的“unsigned char I2CReadNAK()”和“unsigned char I2CReadACK()”,其实这两个函数只有一处不同,那就是“I2C_SDA = 1;”和“I2C_SDA = 0;”是否产生应答。如果大家一句句地很难理解这个函数,那么不妨直接省去理解,拿来运用就可以了。

3.独立代码

在main.c里复制以下代码,编译下载进开发板,我们做的实验就是先在EEPROM的0x55的地址里写入数据71(也就是字符‘G’的ASCII码值),然后再去读出这个数据,将字符‘G’显示在液晶屏上。

之前的章节有提过为了兼容性我们不打算使用bit型的数据类型,所以修改了一下宋老师的“bit I2CWrite(unsigned char dat)”函数,改为unsigned char类型。

那么我们就不能像宋老师那样写为“return (~ack);”,而是写为“return (!ack);”。unsigned char类型下,ack如果是0,那么~ack就为0xFF;ack如果是1,~ack就为0xFE,所以“~ack”的书写方式会导致返回值不能限定在0或者1之间,也就会使代码功能失效。

而“!ack”的表达就是说,当ack等于0时,!ack只会等于1,不会等于0xFF这些。当ack不等于0时(比如“ack=0xFE;”这些),那么!ack只会等于0,这样保证了返回值只有0和1。

#include <reg52.h> 
#include <function.h>//详见第六章第8讲
#include <lcd.h>     //详见第十一章第3讲
#include <intrins.h>
  
#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;
  
/* 产生总线起始信号 */
void I2CStart()
{
    I2C_SDA = 1; //首先确保SDA、SCL都是高电平
    I2C_SCL = 1;
    I2CDelay();
    I2C_SDA = 0; //先拉低SDA
    I2CDelay();
    I2C_SCL = 0; //再拉低SCL
}
 
/* 产生总线停止信号 */
void I2CStop()
{
    I2C_SCL = 0; //首先确保SDA、SCL都是低电平
    I2C_SDA = 0;
    I2CDelay();
    I2C_SCL = 1; //先拉高SCL
    I2CDelay();
    I2C_SDA = 1; //再拉高SDA
    I2CDelay();
}
 
/* I2C总线写操作,dat-待写入字节,返回值-从机应答位的值 */
u8 I2CWrite(unsigned char dat)
{
    u8 ack;                            //用于暂存应答位的值
    unsigned char mask;                //用于探测字节内某一位值的掩码变量
   
    for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
    {
        if ((mask&dat) == 0)  //该位的值输出到SDA上
            I2C_SDA = 0;
        else
            I2C_SDA = 1;
        I2CDelay();
        I2C_SCL = 1;          //拉高SCL
        I2CDelay();
        I2C_SCL = 0;          //再拉低SCL,完成一个位周期
    }
    I2C_SDA = 1;   //8位数据发送完后,主机释放SDA,以检测从机应答
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    ack = I2C_SDA; //读取此时的SDA值,即为从机的应答值
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成应答位,并保持住总线
   
    return (!ack); //应答值取反以符合通常的逻辑:
                   //0=不存在或忙或写入失败,1=存在且空闲或写入成功
}
 
/* I2C总线读操作,并发送非应答信号,返回值-读到的字节 */
unsigned char I2CReadNAK()
{
    unsigned char mask;
    unsigned char dat;
   
    I2C_SDA = 1;                       //首先确保主机释放SDA
    for (mask=0x80; mask!=0; mask>>=1) //从高位到低位依次进行
    {
        I2CDelay();
        I2C_SCL = 1;      //拉高SCL
        if(I2C_SDA == 0)  //读取SDA的值
            dat &= ~mask; //为0时,dat中对应位清零
        else
            dat |= mask;  //为1时,dat中对应位置1
        I2CDelay();
        I2C_SCL = 0;      //再拉低SCL,以使从机发送出下一位
    }
    I2C_SDA = 1;   //8位数据发送完后,拉高SDA,发送非应答信号
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成非应答位,并保持住总线
   
    return dat;
}
  
void main()
{  
    u8 str[2];
    InitLcd1602();//初始化液晶屏 
    while (1)
    {     
        I2CStart();
        I2CWrite(0x50<<1); //寻址器件,后续为写操作
        I2CWrite(0x55);    //写入要存储的地址为0x55
        I2CWrite('G');     //写入71这个数据,'G'的表达更加直观地表示要在液晶屏上显示的内容
        I2CStop();
     
        delay_ms(1000);    //等待一秒之后,准备读出这个数据
     
        I2CStart();
        I2CWrite(0x50<<1); //寻址器件,后续为写操作
        I2CWrite(0x55);    //选择要读出内容的地址为0x55
        I2CStart();        //发送重复启动信号
        I2CWrite((0x50<<1)|0x01); //寻址器件,后续为读操作
        str[0] = I2CReadNAK();    //读取这个器件0x55的地址里面的内容存放在数组的0号元素里
        I2CStop();
     
        LcdShowStr_len(7, 0, str, 1);//显示str[0]的内容
        while (1);//程序暂停运行
    }
}

 

为了更加直观了解这些过程,笔者并没有像宋老师那样给大家封装好写函数和读函数,这是为了大家不用在各个函数中跳来跳去地分析,在主函数里直接一步到位地讲解这些操作步骤,方便大家对这些细节的了解。

 

 

 


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



317 次浏览
1次