/*
* 软件模拟I2C
*
* 一、IIC地址的确定(AT24C系列)
* 1、24c01,02 1K/2K EEPROM 在一条IIC总线上可以挂8个,地址由A2,A1,A0确定;
* 2、24C04 4k EEPROM 只有A2,A1的做地址位,这样一条IIC总线上能挂4个设备,
* A0是用来确定内部页地址的,A0在芯片上没有线连接的(NA);
* 3、24C08, 8k EEPROM 使用A2来确定地址线,A1,A0位是在确定内部页地址的,
* 一条IIC总线能扩展2片;
* 4、24C16 16k,A2A1A0都是确定内部页地址的;一条总线上只能挂1个一个这样的设备.
* 5、32,64中 发送的内部地址都是发2次,高地址和低地址,这样有16位地址位可以确
* 定内部地址,就不需要用A2A1A0来确定地址了
* 二、控制器的读写时序(01---16)
* 读:发设备地址---> 送8位地址---->发设备地址--->读取--->NOACK--->停止;
* 写:发设备地址--->送8位地址----->写数据--->停止;
* 控制器的读写时序(32---64)
* 读:发设备地址--->送高8位地址--->送低8位地址--->发设备地址--->读取--
* --->NOACK--->停止
* 写:发设备地址--->送高8位地址----->送低8位地址---->写数据--->停止;
*/
#include "stm32f10x.h"
#include "stm32f103r8t6.h"
/*Delay_nus(100); delay about 100us*/
#define DELAY 200
/*
* 配置I2C相关GPIO
* 管脚:GPIO_Pin_6 SCL ; GPIO_Pin_7 SDA ;
* 速度:GPIO_Speed_50MHz
* 模式:GPIO_Mode_Out_PP 推挽输出
*/
void I2C_GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
/*释放总线*/
I2C_SCL_High;
I2C_SDA_High;
}
/**/
void SDA_GPIO_Output(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = I2C_SDA;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
/**/
void SDA_GPIO_Input(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = I2C_SDA;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
/*
* I2C 产生开始状态
* I2C_READY = 0
* I2C_BUS_BUSY = 1
*/
void I2C_Start(void)
{
I2C_SDA_High;
I2C_SCL_High;
DelayNus(DELAY);
I2C_SDA_Low;
DelayNus(DELAY);
I2C_SCL_Low;
DelayNus(DELAY);
}
/*
* I2C 产生停止状态
*/
void I2C_Stop(void)
{
I2C_SDA_Low;
DelayNus(DELAY);
I2C_SCL_High;
DelayNus(DELAY);
I2C_SDA_High;
DelayNus(DELAY);
}
/*
* 应答信号(SDA)为低电平时,规定为有效应答位。
*
* 对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线
* 拉低,并且确保在该时钟的高电平期间为稳定的低电平。
*/
void I2C_Ack(void)
{
I2C_SCL_Low;
I2C_SDA_Low;
DelayNus(DELAY);
I2C_SCL_High;
DelayNus(DELAY);
I2C_SCL_Low;
I2C_SDA_High;
}
/*
*
*/
void I2C_NoAck(void)
{
I2C_SCL_Low;
I2C_SDA_High;
DelayNus(DELAY);
I2C_SCL_High;
DelayNus(DELAY);
I2C_SCL_Low;
I2C_SDA_High;
}
/*
* 返回值:0,有Ack; 1,无Ack。
*/
uint8_t I2C_CheckAck(void)
{
uint8_t ack = 1;
SDA_GPIO_Input();
I2C_SCL_Low;
DelayNus(DELAY);
I2C_SCL_High;
DelayNus(DELAY);
if (!SDA_Read())
{
ack = 0;
}
I2C_SCL_Low;
SDA_GPIO_Output();
DelayNus(DELAY);
return ack;
}
/*
* 发送数据时,先发送高位。
*/
void I2C_SendByte(uint8_t Dat)
{
uint8_t i = 8;
while (i--)
{
if (Dat&0x80)
{
I2C_SDA_High;
}
else
{
I2C_SDA_Low;
}
I2C_SCL_High;
DelayNus(DELAY);
Dat <<= 1;
I2C_SCL_Low;
DelayNus(DELAY);
}
DelayNus(DELAY);
}
/*
* 接收数据时,先接收高位。
*/
uint8_t I2C_ReceiveByte(void)
{
uint8_t i = 8;
uint8_t Dat = 0;
SDA_GPIO_Input();
while (i--)
{
Dat <<= 1;
I2C_SCL_High;
DelayNus(DELAY);
if (SDA_Read())
{
Dat |= 0x01;
}
I2C_SCL_Low;
DelayNus(DELAY);
}
SDA_GPIO_Output();
return Dat;
}
/*
* 功 能:向任意地址写入数据
* 入口参数:address-AT24CXX内部存储地址
* 返回 值:0 成功; 1 I2C_Busbusy; 2 I2C_ERROR_NoAck无应答;
* 3 I2C_ERROR_OutOfAddress超出设备地址范围;
* 4 I2C_ERROR_SlaveAddress写器件地址错误
* AT24C16内部有2048*8位的存储容量,即可以存储2K字节的数据。这2K字节被放在
* 128个页内,每页存放16个字节。所以对AT24C16内部的访问需要11位地址(0-7ff)。
* 对AT24C16访问时,按照页地址和页偏移量的方式进行访问。比如要访问第100页的
* 第3个字节,则在发送寻址的时候,就要发送0X0643,其中页地址的高三位放在器件地
* 址中。所以在编写程序对AT24C16第100页的第3个字节进行写数据的时候,步骤如下:
* 1)发送起始信号;
* 2)发送器件地址0X06(0000 0110,011是页地址的高三位,0表示写操作);
* (011=3,在程序中AddressHigh<<1后,011变成110=6)
* 3)发送操作地址0X43(0100 0011,0100是页地址的低四位,0011是页地址偏移量,
* 即第100页内的第三个字节);
* 4)发送要写的数据;
* 5)发送终止信号。
* 第100页第3个字节的Address = 0x0643
*/
uint8_t AT24CXX_WriteByte(uint16_t Address,uint8_t Dat)
{
uint8_t AddressHigh,AddressLow,DeviceAddress;
if(Address > AT24CXX) return I2C_ERROR_OutOfAddress;
AddressHigh = (uint8_t)(Address >> 8);
AddressLow = (uint8_t)(Address);
DeviceAddress = AT24CXX_WriteAddress | (AddressHigh << 1);
I2C_Start();
I2C_SendByte(DeviceAddress);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
I2C_SendByte(AddressLow);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
I2C_SendByte(Dat);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
I2C_Stop();
DelayNms(5);
return 0;
}
/*
* 功 能:从任意地址读一字节数据
* 返回 值:0 成功; 1 I2C_Busbusy; 2 I2C_ERROR_NoAck无应答;
* 3 I2C_ERROR_OutOfAddress超出设备地址范围;
* 4 I2C_ERROR_SlaveAddress写器件地址错误
* 使用时次函数,设uint8_t ucReceiveData[8];
* 调用函数AT24CXX_ReadByte(Address,ucReceiveData);
* ucReceiveData数组中即是读到的数据。
*/
uint8_t AT24CXX_ReadByte(uint16_t Address,uint8_t *Dat)
{
uint8_t AddressHigh,AddressLow,DeviceAddress;
if(Address > AT24CXX)return I2C_ERROR_OutOfAddress;
AddressHigh = (uint8_t)(Address >> 8);
AddressLow = (uint8_t)(Address);
DeviceAddress = AT24CXX_WriteAddress | (AddressHigh << 1);
I2C_Start();
I2C_SendByte(DeviceAddress);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
I2C_SendByte(AddressLow);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
DeviceAddress = AT24CXX_ReadAddress | (AddressHigh << 1);
DelayNus(DELAY);
I2C_Start();
I2C_SendByte(DeviceAddress); /*发送读指令 */
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
*Dat = I2C_ReceiveByte();
I2C_NoAck();
I2C_Stop();
DelayNms(5);
return 0;
}
/*
* 功能:连续读data_long个数据
*/
uint8_t AT24CXX_ReadNByte(uint16_t Address,uint8_t *Dat,uint16_t DataLength)
{
uint8_t AddressHigh,AddressLow,DeviceAddress;
if(Address > AT24CXX)return I2C_ERROR_OutOfAddress;
AddressHigh = (uint8_t)(Address >> 8);
AddressLow = (uint8_t)(Address);
DeviceAddress = AT24CXX_WriteAddress | (AddressHigh << 1);
I2C_Start();
I2C_SendByte(DeviceAddress);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
I2C_SendByte(AddressLow);
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
DeviceAddress = AT24CXX_ReadAddress | (AddressHigh << 1);
DelayNus(DELAY);
I2C_Start();
I2C_SendByte(DeviceAddress); /*发送读指令 */
if(I2C_CheckAck()) return I2C_ERROR_NoAck;
while (DataLength--)
{
*Dat++ = I2C_ReceiveByte();
if (0==DataLength)
{
I2C_NoAck();
break;
}
I2C_Ack();
}
I2C_Stop();
DelayNms(5);
return I2C_Ready;
}
/***************************END OF FILE***************************/