单片机协议调试笔记 --- GPIO和IIC测试
问题背景
最近在调一个IIC陀螺仪,发什么命令都没反应。折腾半天才发现------GPIO引脚根本没动!
原来是移植了正点原子的库,里面的IO操作是F103的位带操作,和F401不兼容。
分享两个实用的测试函数
1. GPIO电平测试 --- 确认引脚真的在动
这个函数用来验证SCL和SDA能不能正常拉高/拉低。
c
void GPIO_Test(void)
{
printf("Starting GPIO test...\r\n");
// 测试SCL引脚(PA8)
printf("Testing SCL (PA8)...\r\n");
IIC_SCL(1);
delay_ms(100);
printf("SCL = 1, PA8_IDR = %d\r\n", HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8));
IIC_SCL(0);
delay_ms(100);
printf("SCL = 0, PA8_IDR = %d\r\n", HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8));
// 测试SDA引脚(PC9)
printf("Testing SDA (PC9)...\r\n");
SDA_OUT();
IIC_SDA(1);
delay_ms(100);
printf("SDA = 1 (output), PC9_IDR = %d\r\n", HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9));
IIC_SDA(0);
delay_ms(100);
printf("SDA = 0 (output), PC9_IDR = %d\r\n", HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9));
printf("GPIO test completed.\r\n");
}
正常应该这样:
Starting GPIO test...
Testing SCL (PA8)...
SCL = 1, PA8_IDR = 1
SCL = 0, PA8_IDR = 0 ← 写0能读到0,说明拉低了
Testing SDA (PC9)...
SDA = 1 (output), PC9_IDR = 1
SDA = 0 (output), PC9_IDR = 0
我当时的情况:
SCL = 0, PA8_IDR = 1 ← 写0还是读到1,根本没拉低!
SDA = 0 (output), PC9_IDR = 1
2. IIC设备扫描 --- 找设备地址
GPIO没问题后,用这个扫描总线上的设备。
c
void I2C_Scan(void)
{
u8 i, ack;
printf("Starting I2C scan...\r\n");
for (i = 0; i < 128; i++)
{
IIC_Start();
IIC_Send_Byte((i << 1) & 0xFE); // 写地址
ack = IIC_Wait_Ack();
IIC_Stop();
if (ack == 0)
{
printf("Found device at 0x%02X (7-bit: 0x%02X)\r\n", (i << 1), i);
}
delay_ms(1);
}
printf("I2C scan completed.\r\n");
}
输出示例:
Starting I2C scan...
Found device at 0xD0 (7-bit: 0x68) ← 找到陀螺仪了,地址0x68
I2C scan completed.
我遇到的坑:F103的位带操作库,不兼容F401
正点原子的库里有这么一段:
c
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define GPIOA_ODR_Addr (GPIOA_BASE+12) // 0x4001080C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) // 0x40010808
...
这些地址是F103的!F401的寄存器布局不一样,直接用会导致IO操作失效。
F103的ODR偏移是+12
F103的IDR偏移是+8
F401的ODR偏移是+20
F401的IDR偏移是+16
解决办法
一:改为F403的位带操作
Cortex-M4编程手册 PM0214 第3.7节 Bit-Banding
c
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
// F401的GPIO寄存器偏移(参考RM0368手册)
#define GPIOA_ODR_Addr (GPIOA_BASE+20) // 0x40020014 ← F401的ODR偏移是+20
#define GPIOA_IDR_Addr (GPIOA_BASE+16) // 0x40020010 ← F401的IDR偏移是+16
...
二:改为HAL函数,更直接,通用
c
// 别用位带了,直接HAL
#define IIC_SCL(n) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, n)
#define IIC_SDA(n) HAL_GPIO_WritePin(GPIOC, GPIO_PIN_9, n)
// 读取SDA切换方向
#define SDA_IN() {GPIOC->MODER &= ~(3 << (9*2)); GPIOC->MODER |= 0 << (9*2);}
#define SDA_OUT() {GPIOC->MODER &= ~(3 << (9*2)); GPIOC->MODER |= 1 << (9*2);}
#define READ_SDA HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_9)
快速排查流程
设备没反应?
↓
跑 GPIO_Test()
↓
写1读1,写0读0? ──否──→ 检查IO配置/上拉电阻/代码兼容性
↓ 是
跑 I2C_Scan()
↓
找到设备地址? ────否──→ 检查接线/供电/设备是否损坏
↓ 是
开始正常读写调试