IIC总线是常用的串行总线,它只需要简单的两根线就可以实现数据的高速传输,同时还可以实现多机通信功能。
在单片机中用的比较多的就是通过IIC总线操作EEPROM芯片。比较常用的EEPROM芯片就是24Cxx系列的芯片,主要用来存储系统运行过程中的关键数据。
要操作这个芯片的话,必须按照一定的时序去读写。这个时序通常被称为通信协议。24Cxx系列芯片通信协议如下。
I2 C 总线协议
I 2 C 总线协议定义如下:
只有在总线空闲时才允许启动数据传送
在数据传送过程中 当时钟线为高电平时 数据线必须保持稳定状态 不允许有跳变 时钟线为高电平时 数据线的任何电平变化将被看作总线的起始或停止信号。
起始信号
时钟线保持高电平期间 数据线电平从高到低的跳变作为 I 2 C 总线的起始信号
停止信号
时钟线保持高电平期间 数据线电平从低到高的跳变作为 I 2 C 总线的停止信号
这里就使用最简单的几个通信时序起始、停止、读、写和应答时序。
为了IIC协议的通用性,将这几个协议封装为一个c文件,这样以后每个使用IIC协议的器件都能调用这个文件。
//IIC产生起始信号
void IIC_Start(void)
{
//START:when CLK is high,DATA change form high to low
SDA_OUT();
IIC_SDA = 1;
IIC_SCL = 1;
delay_us(4);
IIC_SDA = 0;
delay_us(4);
IIC_SCL = 0;
}
//产生停止信号
void IIC_Stop(void)
{
//STOP:when CLK is high DATA change form low to high
SDA_OUT();
IIC_SCL = 0;
IIC_SDA = 0;
delay_us(4);
IIC_SCL = 1;
IIC_SDA = 1;
delay_us(4);
}
//等待应答信号
//返回值: 1 应答失败
// 0 应答成功
u8 IIC_Wait_Ack(void)
{
u8 errTime = 0;
SDA_IN();
IIC_SDA = 1;
delay_us(1);
IIC_SCL = 1;
delay_us(1);
while(READ_SDA)
{
errTime++;
if(errTime > 250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL = 0;
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 0;
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
}
//不产生应答
void IIC_NAck(void)
{
IIC_SCL = 0;
SDA_OUT();
IIC_SDA = 1;
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
}
//IIC发送一个字节
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL = 0;
for(t = 0; t < 8; t++)
{
if((txd & 0x80) >> 7)
IIC_SDA = 1;
else
IIC_SDA = 0;
txd <<= 1;
delay_us(2);
IIC_SCL = 1;
delay_us(2);
IIC_SCL = 0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送NACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i, receive = 0;
SDA_IN();
for(i = 0; i < 8; i++)
{
IIC_SCL = 0;
delay_us(2);
IIC_SCL = 1;
receive <<= 1;
if(READ_SDA)
receive++;
delay_us(1);
}
if(!ack)
IIC_NAck();
else
IIC_Ack();
return receive;
}
这里将最常用的几个信号封装为函数,当操作24Cxx芯片的时候,直接调用这几个函数。
#include "24Cxx.h"
#include "delay.h"
void AT24Cxx_Init(void)
{
IIC_Init();
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值 :读到的数据
u8 AT24Cxx_ReadOneByte(u16 ReadAddr)
{
u8 temp = 0;
IIC_Start();
if(EE_TYPE > AT24C16)
{
IIC_Send_Byte(0xA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr >> 8); //发送高地址
IIC_Wait_Ack();
}
else
IIC_Send_Byte(0xA0 + ((ReadAddr / 256) << 1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr % 256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0xA1); //进入接收模式
IIC_Wait_Ack();
temp = IIC_Read_Byte(0);
IIC_Stop(); //产生一个停止条件
return temp;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24Cxx_WriteOneByte(u16 WriteAddr, u8 DataToWrite)
{
IIC_Start();
if(EE_TYPE > AT24C16)
{
IIC_Send_Byte(0xA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr >> 8); //发送高地址
}
else
IIC_Send_Byte(0xA0 + ((WriteAddr / 256) << 1)); //发送器件地址0XA0,写数据
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr % 256);
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite);
IIC_Wait_Ack();
IIC_Stop();
delay_ms(10);
}
//在AT24CXX里面的指定地址开始写入长度为Len的数据
//该函数用于写入16bit或者32bit的数据.
//WriteAddr :开始写入的地址
//DataToWrite:数据数组首地址
//Len :要写入数据的长度2,4
void AT24Cxx_WriteLenByte(u16 WriteAddr, u32 DataToWrite, u8 Len)
{
u8 t;
for(t = 0; t < Len; t++)
{
AT24Cxx_WriteOneByte(WriteAddr + t, (DataToWrite >> (8 * t)) & 0xff);
}
}
//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr :开始读出的地址
//返回值 :数据
//Len :要读出数据的长度2,4
u32 AT24Cxx_ReadLenByte(u16 ReadAddr, u8 Len)
{
u8 t;
u32 temp = 0;
for(t = 0; t < Len; t++)
{
temp <<= 8;
temp += AT24Cxx_ReadOneByte(ReadAddr + Len - t - 1);
}
return temp;
}
//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24Cxx_Check(void)
{
u8 temp;
temp = AT24Cxx_ReadOneByte(255);
if(temp == 0x55)
return 0;
else
{
AT24Cxx_WriteOneByte(1023, 0x55);
temp = AT24Cxx_ReadOneByte(255);
if(temp == 0x55)
return 0;
}
return 1;
}
//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer :数据数组首地址
//NumToRead:要读出数据的个数
void AT24Cxx_Read(u16 ReadAddr, u8 *pBuffer, u16 NumToRead)
{
while(NumToRead)
{
*pBuffer++ = AT24Cxx_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24Cxx_Write(u16 WriteAddr, u8 *pBuffer, u16 NumToWrite)
{
while(NumToWrite--)
{
AT24Cxx_WriteOneByte(WriteAddr, *pBuffer);
WriteAddr++;
pBuffer++;
}
}
这里就是对具体的芯片操作函数,在主函数中通过这几个函数就可读写EEPROM存储芯片的内容了。
int main(void)
{
u8 key;
u16 i = 0, j = 0;
u8 datatemp[SIZE];
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
uart_init(115200);
LED_Init();
KEY_Init();
AT24Cxx_Init();
printf("IIC test!!!rn");
while(AT24Cxx_Check())
{
printf("The chip is not detected, please check whether the hardware connection is normal!!!rn");
delay_ms(300);
LED1 = !LED1;
}
while(1)
{
key = KEY_Sacn(0);
if(key == WKUP_PRES)
{
printf("rnStart Write 24C02....rn");
AT24Cxx_Write(0, (u8 *)TEXT_Buffer, SIZE);
printf("24C02 Write Finished!rn");
}
else if(key == KEY1_PRES)
{
printf("rnStart Read 24C02....rn");
AT24Cxx_Read(0, datatemp, SIZE);
printf("The Data Readed Is: ");
for(j = 0; j < SIZE; j++)
{
printf("%c", datatemp[j]);
}
}
i++;
if(i == 20)
{
i = 0;
LED0 = !LED0;
}
delay_ms(5);
}
}
这里通过按键来测试存储芯片,一个按键按下后向芯片内写入数据,另一个按键按下后从芯片中读取刚才写入的内容。
上一篇:STM32F103定时器输入捕获功能
下一篇:STM32F103外部中断实现
推荐阅读最新更新时间:2024-11-13 10:40
推荐帖子
- 关于linux自定制,请教-:新手上路
- 新手中的新手!望大家慷慨相助或是提供帮助信息、教程之类的:)想要自定制一个剪裁过的linux系统,现在手里有一套DellOptilex745n系列电脑赠送的redhat安装盘,请问是否能够靠它剪裁出自己希望的小规模系统。目标硬件平台为PC104,CPU:AMD嵌入式,主频500M,内存256M,CF卡容量1G,显示屏为标准的VGA接口LCD。要求:剪裁出的系统具有网络、串口、键盘、显示驱动功能,开发工具Qt3.2,其开发的软件能够在目标机稳定运行,程序主要做显示、键盘参数录入
- huangluling Linux与安卓
- RISC-V处理器设计系列课程——CPU基础知识
- 主要为大家介绍一下CPU相关的基础知识,从CPU发展的角度来看RISC-V的诞生缘由RISC-V处理器设计系列课程——CPU基础知识
- 火辣西米秀 国产芯片交流
- 关于OPA847的自激
- 上面是制成的PCB电路板,反馈电阻接的是滑动变阻器,焊好之后上电测试就发现自激了,没有输入信号的时候就有了输出,大概660MHz,检查了电路板发现也没什么问题。不知道问题出在哪里,哪位老师能帮我看看?关于OPA847的自激@maychang@gmchen这类高带宽高增益运放,本来就是很难使用的,必须严格按照说明书中所指示的注意事项使用。不像LM358之类,电路板无论怎么布局大致上都稳定工作。把电原理图贴出来,请教gmchen老师吧。我在这方面经验不多。没看到原理图,就看到的实
- Alexline 模拟电子
- 用ST的JOYSTICK例程改的程序,端点3无**常发送数据
- UserToPMABufferCopy(Key_Buffer,GetEPTxAddr(ENDP1),9);//*enableendpointfortransmissionSetEPTxCount(ENDP1,9);SetEPTxValid(ENDP1);用端点1能正常发送数据,但把上述ENDP1改成ENDP3,发送了9个随机数。设置如下:/*txbufferbaseaddress*/#defineENDP1_TXADDR(0x100)#d
- wang1jin stm32/stm8
- 点阵屏的任意级联原理
- 利用75HC595串行移动功能和存功能,支持理论上任意长度级联,不知道正确否?欢迎大家指正点阵屏的任意级联原理条件是没有寄生电感和电容过长时可以中间加245做驱动楼上兄弟都做过LED显示屏的吧近期打算做过LED显示控制器,希望大家多多帮组
- eeleader 工控电子
- MF RC500
- MFRC500是应用于13.56MHz非接触式通信中高集成读卡IC系列中的一员。该读卡IC系列利用了先进的调制和解调概念,完全集成了在13.56MHz下所有类型的被动非接触式通信方式和协议。MFRC500支持ISO14443A所有的层。内部的发送器部分不需要增加有源电路就能够直接驱动近操作距离的天线(可达100mm)。接收器部分提供一个坚固而有效的解调和解码电路,用于ISO14443A兼容的应答器信号。数字部分处理ISO14443A帧和错误检测(奇偶&CRC)。此外,它还支持快速CR
- rain RF/无线
设计资源 培训 开发板 精华推荐
- LT1934IS6 1.8V 降压转换器的典型应用电路
- 使用 Analog Devices 的 LT3470IDDB 的参考设计
- A8586KLJTR-T 宽输入电压、可调频率、3.5A 降压稳压器的典型应用电路,显示关键路径/环路
- 使用 Analog Devices 的 LTC1867LACGN 的参考设计
- #第八届立创电赛#电子钟实现
- LTC4269-1 演示板,带有 48V 隔离辅助装置的 PoE 受电设备,Vout = 12V,Iout = 2A
- TA8210ALQ 20W BTL x 2ch 音频功率放大器典型应用
- 补偿 ADXL50 加速度/温度输出的 0g 偏移漂移
- Cube-443_示波镊子
- LT1108CS8-12掌上电脑逻辑电源微功率DC/DC转换器典型应用电路
- 6月6日 Microchip 直播|利用单片机设计安全关键型应用时应采取的最佳实践方法
- 双11之单片机狂欢:11.11元包邮拇指板STM32L011,抢完为止
- LPC8N04测评,激发你的NFC&物联网新创意!
- 快速获取TI 工业电机驱动资源 答题赢好礼
- 打卡英飞凌碳化硅MOSFET新品快闪店
- 畅游安富利人工智能云会展,挑战60天打卡学习养成记!冲击华为Mate40 Pro、Apple iPad Air等豪礼啦!
- 下载Mentor白皮书,迎接电路板与晶片日益复杂的设计挑战,还有好礼相送哟!
- 有奖直播 11月27日上午10:00 准时开启!富士通赋能汽车电子技术变革
- 有奖直播:Microchip适用于CryptoAuthentication™系列的可信任平台