单片机调试IIC笔记 — 首先检测GPIO和协议能不能用

单片机协议调试笔记 --- 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()
    ↓
找到设备地址? ────否──→ 检查接线/供电/设备是否损坏
    ↓ 是
开始正常读写调试
相关推荐
朝发如雪3 小时前
快速掌握单片机基础(3)(PWM)(通信协议基础)(串口通信时序与参数)(电气特性与电平标准)(51单片机UART寄存器配置)(串口应用层协议)
单片机
天空'之城5 小时前
单片机基础核心知识点汇总(四十)
单片机·嵌入式硬件
smartpi_ai5 小时前
离线语音模块误识别问题全面解决方案:命令词设计、防误触与灵敏度调优指南
人工智能·单片机·语音识别
LCG元5 小时前
STM32F103 驱动 HX711 称重实战:DOUT 就绪时序、增益切换陷阱与两点标定精度实测
stm32·单片机·嵌入式硬件
染予6 小时前
代码中相关C语言知识点总结
stm32
优信电子6 小时前
STM32 驱动 DHT30 测量环境温湿度(兼容SHT30):从时序解析到完整代码实战
stm32·嵌入式·传感器·sht30·温湿度·环境测量·dht30
小李不想当小白8 小时前
DMA直接存储器存取(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·分享
朝发如雪8 小时前
快速掌握单片机基础(6)(数据处理与传送指令)(跳转与分支指令)(栈操作与混合编程)(程序状态寄存器操作指令)(内存映射)
单片机·arm
blue_ice .8 小时前
Verilog DDS 数字信号发生器:DAC 输出模拟波形与 ADC 回采观测
开发语言·单片机·嵌入式硬件·fpga开发
远翔调光芯片^138287988729 小时前
ECP5702能芯科技PD取电芯片在市场上的优势有哪些?
开发语言·人工智能·单片机·嵌入式硬件·智能家居