一、IIC
基于stm32F103 软件模拟IIC。
IIC_SCL和IIC_SDA分别复用PB6和PB7 IO口。
二、具体代码
1.头文件
#ifndef __IIC_SW_H
#define __IIC_SW_H
//GPIOx ODR和IDR寄存器地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+0x0C) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+0x0C) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+0x0C) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+0x0C) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+0x0C) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+0x0C) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+0x0C) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+0x08) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+0x08) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+0x08) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+0x08) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+0x08) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+0x08) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+0x08) //0x40011E08
//位段操作
/*外设位带别名区地址0x42000000 ~ 0x43FFFFFF*/
#define BIT_BAND(addr, pin_num) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEE_ADDR(addr) *((volatile unsigned long*)addr)
#define BIT_ADDR(addr, pin_num) MEE_ADDR(BIT_BAND(addr, pin_num))
#define PBout(pin_num) BIT_ADDR(GPIOB_ODR_Addr, pin_num)
#define PBin(pin_num) BIT_ADDR(GPIOB_IDR_Addr, pin_num)
// IO 操作
#define I2C_SCL PBout(6) //PB6 SCL
#define I2C_SDA PBout(7) //PB7 SDA
#define READ_SDA PBin(7) //PB7 输入SDA
#define SDA_IN() {GPIOB->CRL &= 0x0FFFFFFF; GPIOB->CRL |= 0x08<<28;} //上下拉输入
#define SDA_OUT() {GPIOB->CRL &= 0x0FFFFFFF; GPIOB->CRL |= 0x07<<28;} //开漏输出
#endif
2.源文件
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_SCL = 1; //SCL和SAD均处于空闲状态
I2C_SDA = 1;
}
//IIC起始信号
void IIC_Start(void)
{
SDA_OUT();
I2C_SCL = 1;
I2C_SDA = 1;
delay_us(4); //延时大概4us
I2C_SDA = 0;
delay_us(4);
I2C_SCL = 0; //将SCL拽下来,准备将要发送或接受的数据
}
//IIC结束信号
void IIC_Stop(void)
{
SDA_OUT();
I2C_SCL = 0;
I2C_SDA = 0;
delay_us(4);
I2C_SCL = 1;
I2C_SDA = 1;
delay_us(4);
}
//等待ACK
//返回值:0-接受应答失败;1-接受应答成功
uint8_t IIC_Wait_Ack(void)
{
uint8_t ErrCnt = 0;
SDA_IN(); //SDA设置为输入。主机接受从机的应答信号
I2C_SDA = 1; // 主机释放SDA,从机获得SDA控制权
I2C_SCL = 1; //SCL高电平期间读数据
while(READ_SDA)
{
ErrCnt ++;
if(ErrCnt > 200){
IIC_Stop();
return 0;
}
}
I2C_SCL = 0;
return 1;
}
//产生ACK应答
void IIC_ACK(void)
{
SDA_OUT();
I2C_SCL = 0;
I2C_SDA = 0;
delay_us(2);
I2C_SCL = 1;
delay_us(2);
I2C_SCL = 0;
}
//产生NACK应答
void IIC_NACK(void)
{
SDA_OUT();
I2C_SCL = 0;
I2C_SDA = 1;
delay_us(2);
I2C_SCL = 1;
delay_us(2);
I2C_SCL = 0;
}
//发送一个字节
void IIC_Send_Byte(uint8_t trd)
{
SDA_OUT();
I2C_SCL = 0; //拉低SCL,进行数据准备
for(uint8_t i = 0; i<8; i++)
{
I2C_SDA = (trd & 0x80)>>7; //数据 高位先行
trd <<= 1;
delay_us(2); //以下三个延时是必须的,需要给从机读数据的时间。
I2C_SCL = 1;
delay_us(2);
I2C_SCL = 0;
delay_us(2);
}
}
//接收一个字节。ack=1时,发送Ack; ack=0时,发送NAck
uint8_t IIC_Read_Byte(bool ack)
{
uint8_t receive = 0;
SDA_IN();
for(uint8_t i=0; i<8; i++)
{
I2C_SCL = 0; //从机进行数据准备
delay_us(2);
I2C_SCL = 1;
receive <<=1;
if(READ_SDA)
receive ++;
delay_us(2);
}
if(ack)
IIC_ACK();
else
IIC_NACK();
return receive ;
}
void IIC_WriteByte(uint8_8 device_addr, uint8_t addr, uint8_t data)
{
IIC_Start();
IIC_Send_Byte(device_addr);
IIC_Wait_Ack();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Send_Byte(data);
IIC_Wait_Ack();
IIC_Stop();
delay_us(2);
}
void IIC_ReadBytes(uint8_8 device_addr, uint8_t addr, uint8_t*data)
{
IIC_Start();
IIC_Send_Byte(device_addr);
IIC_Wait_Ack();
IIC_Send_Byte(addr);
IIC_Wait_Ack();
IIC_Start(); //repeat start
IIC_Send_Byte(device_addr);
IIC_Wait_Ack();
*data = IIC_Read_Byte(0); // NAck
IIC_Stop();
delay_us(2);
}
总结
重点在于IIC的读写时序图