[已验证]基于HAL库可迁移通用gpio模拟I2C实现
引言
之前写过一版,但没有经过工程验证,而今年在实际工程领域确实要用到gpio模拟I2C的功能实现,我在原有的基础上调试,修复bug才有了今天这篇博客。下述源码已经过实际开发工程验证,可迁移使用。话不多说,在介绍一遍吧。。
I2C协议简介
I2C是一种多主多从的串行通信协议,具有简单、灵活的优点。它使用两根信号线:SDA(数据线)和SCL(时钟线)。数据在SDA线上传输,而SCL线用于同步信号。I2C的通信时序如下图所示:
时序说明
- 起始条件(Start Condition):SDA从高电平跳变到低电平,SCL保持高电平。
- 停止条件(Stop Condition):SDA从低电平跳变到高电平,SCL保持高电平。
- 数据传输:数据在SDA线上传输时,SCL线提供时钟信号,每个时钟周期传输一位数据。
GPIO初始化
在STM32中,我使用GPIO引脚模拟I2C信号。以下是GPIO初始化的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void I2C_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C3_SCL_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(I2C3_SCL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = I2C3_SDA_Pin; HAL_GPIO_Init(I2C3_SDA_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(I2C3_SCL_GPIO_Port, I2C3_SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C3_SDA_GPIO_Port, I2C3_SDA_Pin, GPIO_PIN_SET); }
|
在I2C_GPIO_Init
函数中,我们将SCL和SDA引脚初始化为推挽输出模式,并将其状态设置为高电平。
I2C操作实现
接下来,我们将实现I2C的基本操作,包括起始条件、停止条件、发送字节和读取字节等。
起始条件与停止条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| void I2C_Start(void) { I2C_Set_SDA(GPIO_PIN_SET); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SDA(GPIO_PIN_RESET); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay(); }
void I2C_Stop(void) { I2C_Set_SDA(GPIO_PIN_RESET); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SDA(GPIO_PIN_SET); I2C_Delay(); }
|
在I2C_Start
和I2C_Stop
函数中,我们通过控制SDA和SCL的电平来实现I2C的起始和停止条件。
发送字节
发送字节的函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| static HAL_StatusTypeDef I2C_Send_Byte(uint8_t byte) { for (int8_t i = 7; i >= 0; i--) { GPIO_PinState bit = (byte & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET; I2C_Set_SDA(bit); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay(); }
I2C_Set_SDA_Input(); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); GPIO_PinState ack = I2C_Read_SDA(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay();
return (ack == GPIO_PIN_RESET) ? HAL_OK : HAL_ERROR; }
|
在I2C_Send_Byte
函数中,我们将数据的每一位发送到SDA线上,并在发送完毕后读取ACK信号。
读取字节
读取字节的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| static uint8_t I2C_Read_Byte(GPIO_PinState ack) { uint8_t byte = 0;
I2C_Set_SDA_Input(); I2C_Delay();
for (int8_t i = 7; i >= 0; i--) { I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); if (I2C_Read_SDA() == GPIO_PIN_SET) { byte |= (1 << i); } I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay(); }
I2C_Set_SDA(ack); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay();
return byte; }
|
在I2C_Read_Byte
函数中,我们设置SDA为输入模式,读取数据位,并根据需要发送ACK或NACK。
I2C读写操作的封装
我们还可以将上述基本操作封装为更高层次的I2C读写函数,如G_I2C_Mem_Write
和G_I2C_Mem_Read
。
写入操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| HAL_StatusTypeDef G_I2C_Mem_Write(uint8_t devAddr, uint8_t memAddr, uint8_t *pData, uint16_t size) { I2C_Start();
if (I2C_Send_Byte(devAddr << 1) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte(memAddr) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
for (uint16_t i = 0; i < size; i++) { if (I2C_Send_Byte(pData[i]) != HAL_OK) { I2C_Stop(); return HAL_ERROR; } }
I2C_Stop(); return HAL_OK; }
|
读取操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| HAL_StatusTypeDef G_I2C_Mem_Read(uint8_t devAddr, uint8_t memAddr, uint8_t *pData, uint16_t size) { I2C_Start();
if (I2C_Send_Byte(devAddr << 1) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte(memAddr) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
I2C_Start();
if (I2C_Send_Byte((devAddr << 1) | 0x01) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
for (uint16_t i = 0; i < size; i++) { pData[i] = I2C_Read_Byte((i == size - 1) ? I2C_NACK : I2C_ACK); }
I2C_Stop(); return HAL_OK; }
|
结论
通过以上实现,我们可以利用GPIO成功模拟I2C通信。这种方法不仅适用于STM32等微控制器,还能应用于其他许多嵌入式平台。在实际应用中,理解I2C时序和信号的处理都是至关重要的。
附录
完整的代码实现可参考本博客提供的源文件和头文件。
i2c_gpio.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
#ifndef __I2C_GPIO_H #define __I2C_GPIO_H
#include "stm32f4xx_hal.h"
void I2C_GPIO_Init(void); void I2C_Delay(void); void I2C_Start(void); void I2C_Stop(void); HAL_StatusTypeDef I2C_Mem_Write(uint8_t devAddr, uint16_t memAddr, uint8_t *pData, uint16_t size); HAL_StatusTypeDef I2C_Mem_Read(uint8_t devAddr, uint16_t memAddr, uint8_t *pData, uint16_t size);
HAL_StatusTypeDef G_I2C_Mem_Write(uint8_t devAddr, uint8_t memAddr, uint8_t *pData, uint16_t size); HAL_StatusTypeDef G_I2C_Mem_Read(uint8_t devAddr, uint8_t memAddr, uint8_t *pData, uint16_t size);
#endif
|
i2c_gpio.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
|
#include "stm32f4xx_hal.h"
#define I2C3_SCL_Pin GPIO_PIN_8 #define I2C3_SDA_Pin GPIO_PIN_9 #define I2C3_SCL_GPIO_Port GPIOA #define I2C3_SDA_GPIO_Port GPIOC
#define I2C_ACK GPIO_PIN_RESET #define I2C_NACK GPIO_PIN_SET
void I2C_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C3_SCL_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(I2C3_SCL_GPIO_Port, &GPIO_InitStruct);
GPIO_InitStruct.Pin = I2C3_SDA_Pin; HAL_GPIO_Init(I2C3_SDA_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(I2C3_SCL_GPIO_Port, I2C3_SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C3_SDA_GPIO_Port, I2C3_SDA_Pin, GPIO_PIN_SET); }
void I2C_Set_SDA_Output(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C3_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(I2C3_SDA_GPIO_Port, &GPIO_InitStruct); }
void I2C_Set_SDA_Input(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C3_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(I2C3_SDA_GPIO_Port, &GPIO_InitStruct); }
static void I2C_Set_SCL(GPIO_PinState state) { HAL_GPIO_WritePin(I2C3_SCL_GPIO_Port, I2C3_SCL_Pin, state); }
static void I2C_Set_SDA(GPIO_PinState state) { I2C_Set_SDA_Output(); HAL_GPIO_WritePin(I2C3_SDA_GPIO_Port, I2C3_SDA_Pin, state); }
static GPIO_PinState I2C_Read_SDA(void) { I2C_Set_SDA_Input(); return HAL_GPIO_ReadPin(I2C3_SDA_GPIO_Port, I2C3_SDA_Pin); }
void I2C_Delay(void) { for (volatile int i = 0; i < 126; i++) { __NOP(); } }
void I2C_Start(void) { I2C_Set_SDA(GPIO_PIN_SET); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SDA(GPIO_PIN_RESET); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay(); }
void I2C_Stop(void) { I2C_Set_SDA(GPIO_PIN_RESET); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SDA(GPIO_PIN_SET); I2C_Delay(); }
static HAL_StatusTypeDef I2C_Send_Byte(uint8_t byte) { for (int8_t i = 7; i >= 0; i--) { GPIO_PinState bit = (byte & (1 << i)) ? GPIO_PIN_SET : GPIO_PIN_RESET; I2C_Set_SDA(bit); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay(); }
I2C_Set_SDA_Input(); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); GPIO_PinState ack = I2C_Read_SDA(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay();
return (ack == GPIO_PIN_RESET) ? HAL_OK : HAL_ERROR; }
static uint8_t I2C_Read_Byte(GPIO_PinState ack) { uint8_t byte = 0;
I2C_Set_SDA_Input(); I2C_Delay();
for (int8_t i = 7; i >= 0; i--) { I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); if (I2C_Read_SDA() == GPIO_PIN_SET) { byte |= (1 << i); } I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay(); }
I2C_Set_SDA(ack); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_SET); I2C_Delay(); I2C_Set_SCL(GPIO_PIN_RESET); I2C_Delay();
return byte; }
HAL_StatusTypeDef G_I2C_Mem_Write(uint8_t devAddr, uint8_t memAddr, uint8_t *pData, uint16_t size) { I2C_Start();
if (I2C_Send_Byte(devAddr << 1) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte(memAddr) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
for (uint16_t i = 0; i < size; i++) { if (I2C_Send_Byte(pData[i]) != HAL_OK) { I2C_Stop(); return HAL_ERROR; } }
I2C_Stop(); return HAL_OK; }
HAL_StatusTypeDef G_I2C_Mem_Read(uint8_t devAddr, uint8_t memAddr, uint8_t *pData, uint16_t size) { I2C_Start();
if (I2C_Send_Byte(devAddr << 1) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte(memAddr) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
I2C_Start();
if (I2C_Send_Byte((devAddr << 1) | 0x01) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
for (uint16_t i = 0; i < size; i++) { pData[i] = I2C_Read_Byte((i == size - 1) ? I2C_NACK : I2C_ACK); }
I2C_Stop(); return HAL_OK; }
HAL_StatusTypeDef I2C_Mem_Write(uint8_t devAddr, uint16_t memAddr, uint8_t *pData, uint16_t size) { I2C_Start();
if (I2C_Send_Byte(devAddr << 1) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte((uint8_t)(memAddr >> 8)) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte((uint8_t)(memAddr & 0xFF)) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
for (uint16_t i = 0; i < size; i++) { if (I2C_Send_Byte(pData[i]) != HAL_OK) { I2C_Stop(); return HAL_ERROR; } }
I2C_Stop(); return HAL_OK; }
HAL_StatusTypeDef I2C_Mem_Read(uint8_t devAddr, uint16_t memAddr, uint8_t *pData, uint16_t size) { I2C_Start();
if (I2C_Send_Byte(devAddr << 1) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte((uint8_t)(memAddr >> 8)) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
if (I2C_Send_Byte((uint8_t)(memAddr & 0xFF)) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
I2C_Start();
if (I2C_Send_Byte((devAddr << 1) | 0x01) != HAL_OK) { I2C_Stop(); return HAL_ERROR; }
for (uint16_t i = 0; i < size; i++) { pData[i] = I2C_Read_Byte((i == size - 1) ? I2C_NACK : I2C_ACK); }
I2C_Stop(); return HAL_OK; }
|