本文介绍如何使用STM32标准外设库的GPIO端口模拟IIC,本例程使用PB6和PB7模拟一路IIC。
本文适合对单片机及C语言有一定基础的开发人员阅读,MCU使用STM32F103VE系列。
IIC (Inter-Integrated Circuit)总线,也可写作I2C,是PHILIPS 公司开发的两线式串行总线,用于多设备之间通讯,分为主机Master和从机Slave,主机和从机可以有多个,但一般情况下只有一个主机,从机之间可以通过地址进行区分,不同种类的设备地址不同,如果同时接入多个相同种类的设备,可以通过片选信号对从机进行选择。通讯只能由主机发起,支持的操作分为读取和写入,即主机读取从机的数据,以及向从机写入数据。
I2C两线分别是时钟线SCL和数据线SDA,其中SCL和SDA均由主机控制,可以设置成开漏输出模式。
标准模式传输速率为100kbit/s,即10us可以传输一个bit,如果用GPIO模拟I2C时电平变换时需要增加适当的延时。
初始化跟普通GPIO类似,只是输出模式设置为开漏输出。
其中SCL始终输出信号,但SDA需要支持输出信号和读取信号,当设置为开漏输出时,如果需要输出信号,则正常输出即可,如果需要读取信号,则MCU将SDA输出高电平,此时如果从机输出低电平,则SDA被拉低,此时MCU可以读到低电平。即GPIO引脚为开漏输出模式时,MCU输出高电平时,即释放了该引脚的控制,此时该引脚的电平取决于从机的输出,且MCU仍可以读取该引脚的电平。
GPIO初始化完成之后,可以将SCL和SDA置为高电平,即释放该引脚的控制,如果总线上有多个主机,则不会干扰其他设备的通讯。
需要按照通讯信号的时序,实现START、STOP、ACK、NACK、Read、Write和WaitAck信号。
1 #define IIC_SCL_1 GPIO_SetBits(GPIOB, GPIO_Pin_6) /* SCL = 1 */ 2 #define IIC_SCL_0 GPIO_ResetBits(GPIOB, GPIO_Pin_6) /* SCL = 0 */ 3 4 #define IIC_SDA_1 GPIO_SetBits(GPIOB, GPIO_Pin_7) /* SDA = 1 */ 5 #define IIC_SDA_0 GPIO_ResetBits(GPIOB, GPIO_Pin_7) /* SDA = 0 */ 6 7 #define IIC_READ_SDA() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7) /* 读SDA口线状态 */ 8 9 //初始化IIC 10 void IIC_Init(void) 11 { 12 GPIO_InitTypeDef GPIO_InitStructure; 13 14 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); 15 16 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; 17 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ; //开漏输出 18 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 19 GPIO_Init(GPIOB, &GPIO_InitStructure); 20 21 IIC_Stop(); 22 } 23 24 //产生IIC起始信号 25 //SCL为高电平时SDA由高变低 26 /* 27 SCL: ̄ ̄ ̄ ̄ ̄\_ 28 SDA: ̄ ̄\____ 29 */ 30 void IIC_Start(void) 31 { 32 IIC_SDA_1; 33 IIC_SCL_1; 34 delay_us(4); 35 IIC_SDA_0; 36 delay_us(4); 37 IIC_SCL_0; 38 } 39 40 //产生IIC停止信号 41 //SCL为高电平时SDA由低变高 42 //IIC空闲时SCL和SDA均输出高电平,这样不会干扰其他设备的收发 43 /* 44 SCL: ̄ ̄ ̄ ̄ 45 SDA:__/ ̄ 46 */ 47 void IIC_Stop(void) 48 { 49 IIC_SDA_0; 50 IIC_SCL_1; 51 delay_us(4); 52 IIC_SDA_1; 53 } 54 55 //等待应答信号到来 56 //返回值:1,接收应答失败 57 // 0,接收应答成功 58 uint8_t IIC_WaitAck(void) 59 { 60 uint8_t errCount = 0; 61 uint8_t ack = 0; 62 63 IIC_SDA_1; 64 delay_us(4); 65 IIC_SCL_1; 66 delay_us(4); 67 68 while(IIC_READ_SDA()) 69 { 70 errCount++; 71 if(errCount > 250){ 72 ack = 1; 73 break; 74 } 75 } 76 IIC_SCL_0; 77 78 return ack; 79 } 80 81 //产生应答ACK 82 //SCL为高电平时SDA为低电平表示应答 83 /* 84 SCL:  ̄ ̄\____ 85 SDA:_______/ ̄ 86 */ 87 void IIC_Ack(void) 88 { 89 IIC_SDA_0; 90 delay_us(4); 91 IIC_SCL_1; 92 delay_us(4); 93 IIC_SCL_0; 94 delay_us(4); 95 IIC_SDA_1; //释放SDA 96 } 97 98 //产生非应答NACK 99 //SCL为高电平时SDA为高电平表示非应答 100 /* 101 SCL:  ̄ ̄\__ 102 SDA: ̄ ̄ ̄ ̄ ̄ ̄ ̄ 103 */ 104 void IIC_NAck(void) 105 { 106 IIC_SDA_1; 107 delay_us(4); 108 IIC_SCL_1; 109 delay_us(4); 110 IIC_SCL_0; 111 delay_us(4); 112 } 113 114 //IIC发送一个字节 115 /* 116 SCL:_ _/ ̄\__ _/ ̄ ̄\__ _/ ̄ ̄\__ _/ ̄ ̄\__ _/ ̄ ̄\__ _/ ̄ ̄\__ _/ ̄ ̄\__ _/ ̄ ̄\__ 117 SDA:-- ------------ -------------- -------------- -------------- -------------- -------------- -------------- -------------- 118 */ 119 void IIC_WriteByte(uint8_t txd) 120 { 121 uint8_t i; 122 123 IIC_SCL_0; 124 for(i = 0; i < 8; i++) 125 { 126 (txd & 0x80) ? IIC_SDA_1 : IIC_SDA_0; 127 txd <<= 1; 128 129 delay_us(4); 130 IIC_SCL_1; 131 delay_us(4); 132 IIC_SCL_0; 133 delay_us(4); 134 } 135 IIC_SDA_1; 136 } 137 138 //读1个字节,ack=1时,发送ACK,ack=0,发送NACK 139 /* 140 SCL: ̄ ̄\__ / ̄ ̄\__ / ̄ ̄\__ / ̄ ̄\__ / ̄ ̄\__ / ̄ ̄\__ / ̄ ̄\__ / ̄ ̄\__ 141 SDA:========== ============ ============ ============ ============ ============ ============ ============ 142 */ 143 uint8_t IIC_ReadByte(uint8_t ack) 144 { 145 uint8_t i, rcv = 0; 146 147 for(i = 0; i < 8; i++) 148 { 149 rcv <<= 1; 150 IIC_SCL_1; 151 delay_us(4); 152 if(IIC_READ_SDA()){ 153 rcv++; 154 } 155 IIC_SCL_0; 156 delay_us(4); 157 } 158 159 ack ? IIC_Ack() : IIC_NAck(); 160 161 return rcv; 162 }
原文:https://www.cnblogs.com/greatpumpkin/p/13507641.html