☰
【STM32标准库】铁道山羊--[I2C]软I2C
2026/10/11 5:19:48 网站建设 项目流程

111一.为什么使用软I2C?

正常的I2C的使用操作繁琐,并且引脚位置受到限制。

软I2C:通过按照I2C的数据帧格式发送波形,可以在单片机上模拟出软件的I2C。

二.软I2C的使用步骤:

1.对所用IO引脚初始化(这里用的是PA0 | PA1)

void My_SI2C_Init(void); //1.初始化GPIOA_Pin_0 | GPIOA_Pin_1 void My_SI2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_SI2CA; GPIO_SI2CA.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1; GPIO_SI2CA.GPIO_Mode=GPIO_Mode_Out_OD;//通用输出开漏 GPIO_SI2CA.GPIO_Speed=GPIO_Speed_2MHz; GPIO_Init(GPIOA,&GPIO_SI2CA); }

使用通用输出开漏模式是因为通用输出开漏模式初始为高电压。

2.IO读写和延迟函数

因为要通过软件的方式去模拟scl和sad的输出波形,因此进行以下操作;

void scl_write(uint8_t level);//向scl写0/1 void sda_write(uint8_t level);//向sda写0/1 uint8_t sda_read(void);//读sad的值 void delay_us(uint32_t us);//微秒延迟函数 void scl_wirte(niut8_t level) { //PA0 if(level==0) { GPIO_WirteBit(GPIOA,GPIO_Pin_0,Bit_RESET); } else { GPIO_WirteBit(GPIOA,GPIO_Pin_0,Bit_SET); } } void sda_write(uint8_t level) { //PA1 if(level==0) { GPIO_WirteBit(GPIOA,GPIO_Pin_1,Bit_RESET); } else { GPIO_WirteBit(GPIOA,GPIO_Pin_1,Bit_SET); } } uint8_t sad_read(void) { if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==Bit_SET)//接口作用从引脚读0/1 { return 1; } else { return 0; } } void delay_us(niut32_t us) { uint32_t n=us*8; for(uint32_t i=0;i<n;i++); }

3.发送起始位和停止位

起始位:在scl为高电压的时候,在sda上出现一个下降沿。

实现:假设起始位发送之前,总线处于空闲状态(此时,scl和sda上都为高低压),那么只需在sda上出现一个低电压

void SendStart(void); void SendStart(void) { sda_write(0);//向sda写0 delay_us(1);//延迟1微秒 }

停止位:在scl为高电压的时候,在sda上出现一个上升沿。

实现:假设在停止位发送之前,scl处于高电压,而sda上电压不确定。可以先向scl和sda上发送一个低电压,延迟后;再向scl上发送一个高电压,再延迟;最后往sda上发送一个高电压,模拟上升沿。

void SendStop(void); void SendStop(void) { scl_write(0); sda_write(0); delay_us(1); scl_write(1); delay_us(1); sda_write(1); delay_us(1); }

4.如何发送一个字节

想要发送一个字节要先知道如何发送一个比特位;

(1)如图蓝色虚线的两部分:

当scl=0,发送方准备数据

当scl=1,接收方读取数据

(2)发送一个比特位的代码

scl_write(0); sda_write(x);//x为要发送的0/1 delay_us(1); scl_write(1); delay_us(1);

(3)声明一个函数用来发送字节

uint8_t SendByte(uint8_t Byte);//参数Byte表示发送字节的内容,返回值 0-->ack 1-->nak

(4)如何得到x

for(int8_t i=7;i>=0;i--) { scl_write(0); if((Byte&(0x01<<i))!=0) { sda_write(1); } else { sda_write(0); } delay_us(1); scl_write(1); delay_us(1); }

(5)检测从机发送的是ack还是nak(波形比较复杂没讲,按波形编程即可)

//读取ACK或NAK scl_write(0); sda_write(1);//释放sda delay_us(1); scl_write(1);//延迟后,再拉高scl delay_us(1); return sda_read();

(6)发送一个字节总代码

uint8_t SendByte(uint8_t Byte);//参数Byte表示发送字节的内容,返回值 0-->ack 1-->nak uint8_t SendByte(uint8_t Byte) { for(int8_t i=7;i>=0;i--) { scl_write(0); if((Byte&(0x01<<i))!=0) { sda_write(1); } else { sda_write(0); } delay_us(1); scl_write(1); delay_us(1); } //读取ACK或NAK scl_write(0); sda_write(1);//释放sda delay_us(1); scl_write(1); delay_us(1); return sda_read(); }

5.如何接收一个字节

(1)向scl写0,向sda写1,释放sda。延迟后,在scl为高电压时,读sda的值

scl_write(0); sda_write(1);//释放sda delay_us(1); scl_write(1); delay_us(1); bit=sda_read();

(2)获取字节

uint8_t bit=0; for(int8_t i=7;i>=0;i--) { scl_write(0); sda_write(1); delay_us(1); scl_write(1); delay_us(1); if(sda_read()!=0) { byte |=(0x01<<i); } }

(3)回复ACK或NAK

scl_write(0); sda_write(!Ack); delay_us(1); scl_write(1); delay_us(1); return byte;

(4)接收总代码

uint8_t ReceiveByte(uint8_t Ack); uint8_t ReceiveByte(uint8_t Ack) { uint8_t bit=0; for(int8_t i=7;i>=0;i--) { scl_write(0); sda_write(1); delay_us(1); scl_write(1); delay_us(1); if(sda_read()!=0) { byte |=(0x01<<i); } } scl_write(0); sda_write(!Ack); delay_us(1); scl_write(1); delay_us(1); return byte; }

6.综合

与硬件I2C一样

int My_SI2C_SendBytes(uint8_t Addr,uint8_t *pData,uint16_t Size) { SendStart(); if(SendByte(Addr&0xfe)!=0) { SendStop(); return -1; } for(uint32_t i=0;i<Size;i++) { if(SendByte(pData[i])!=0) { SendStop(); return -2; } } SendByte(); }
int My_SI2C_ReceiveBytes(uint8_t Addr,uint8_t *pBuffer,uint16_t Size) { SendStart(); if(SendByte(Addr|0x01)!=0) { SendStop(); return -1; } for(uint32_t i=0;i<Size-1;i++) { pBuffer[i]=ReceivByte(1); } PBuffer[Size-1ReceivByte(0); SendStop(); }

7.实现(上面有错误,这是改正且能编译的)

#include "stm32f10x.h" void My_SI2C_Init(void); void scl_write(uint8_t level);//向scl写0/1 void sda_write(uint8_t level);//向sad写0/1 uint8_t sda_read(void);//读sad电压 void delay_us(uint32_t us);//微秒延迟函数 void SendStart(void); void SendStop(void); uint8_t SendByte(uint8_t Byte);//参数Byte表示发送字节的内容 uint8_t ReceiveByte(uint8_t Ack); int My_SI2C_SendBytes(uint8_t Addr,uint8_t *pData,uint16_t Size); int My_SI2C_ReceiveBytes(uint8_t Addr,uint8_t *pBuffer,uint16_t Size); int main(void) { My_SI2C_Init(); uint8_t commands[]={0x00,0x8d,0x14,0xaf,0xa5}; My_SI2C_SendBytes(0x78,commands,5); while(1) { } } //1.初始化GPIOA_Pin_0 | GPIOA_Pin_1 void My_SI2C_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); GPIO_InitTypeDef GPIO_SI2CA; GPIO_SI2CA.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1; GPIO_SI2CA.GPIO_Mode=GPIO_Mode_Out_OD; GPIO_SI2CA.GPIO_Speed=GPIO_Speed_2MHz; GPIO_Init(GPIOA,&GPIO_SI2CA); GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//初始高压 GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);//初始高压 } void scl_write(uint8_t level) { //PA0 if(level==0) { GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); } else { GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET); } } void sda_write(uint8_t level) { //PA1 if(level==0) { GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET); } else { GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET); } } uint8_t sda_read(void) { if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==Bit_SET)//接口作用从引脚读0/1 { return 1; } else { return 0; } } void delay_us(uint32_t us) { uint32_t n=us*8; for(uint32_t i=0;i<n;i++); } void SendStart(void) { sda_write(0);//向sda写0 delay_us(1);//延迟1微秒 } void SendStop(void) { scl_write(0); sda_write(0); delay_us(1); scl_write(1); delay_us(1); sda_write(1); delay_us(1); } uint8_t SendByte(uint8_t Byte) { for(int8_t i=7;i>=0;i--) { scl_write(0); if((Byte&(0x01<<i))!=0) { sda_write(1); } else { sda_write(0); } delay_us(1); scl_write(1); delay_us(1); } //读取ACK或NAK scl_write(0); sda_write(1);//释放sda delay_us(1); scl_write(1); delay_us(1); return sda_read(); } uint8_t ReceiveByte(uint8_t Ack) { uint8_t byte=0; for(int8_t i=7;i>=0;i--) { scl_write(0); sda_write(1); delay_us(1); scl_write(1); delay_us(1); if(sda_read()!=0) { byte |=(0x01<<i); } } scl_write(0); sda_write(!Ack); delay_us(1); scl_write(1); delay_us(1); return byte; } int My_SI2C_SendBytes(uint8_t Addr,uint8_t *pData,uint16_t Size) { SendStart(); if(SendByte(Addr&0xfe)!=0) { SendStop(); return -1; } for(uint32_t i=0;i<Size;i++) { if(SendByte(pData[i])!=0) { SendStop(); return -2; } } SendStop(); return 0; } int My_SI2C_ReceiveBytes(uint8_t Addr,uint8_t *pBuffer,uint16_t Size) { SendStart(); if(SendByte(Addr|0x01)!=0) { SendStop(); return -1; } for(uint32_t i=0;i<Size-1;i++) { pBuffer[i]=ReceiveByte(1); } pBuffer[Size-1]=ReceiveByte(0); SendStop(); return 0; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询