Stm32软件模拟IIC驱动OLED异常

题主在学习IIC驱动OLED过程中,模仿例程的逻辑编写好代码,程序几乎一致但是OLED啥也显示不出来。通过查询资料选择用逻辑分析仪分析IIC时钟线和数据线的时序信号,检查具体是哪里出了问题。

 时序图显示在每次模拟IIC生成停止信号时,都会多出来一个开始信号,导致IIC信号传输异常,之后具体查询这部分代码。

/**
 * 功能:发起IIC停止信号
 * 参数:None
 * 返回值:None
 */
void stopIIC(void)
{
    IIC_SDA_OUT();
    IIC_SCL_H();
	IIC_SDA_L();
    Delay_us(IIC_SPEED);
    IIC_SDA_H();            //SCL高电平期间,产生SDA下降沿
    Delay_us(IIC_SPEED);
    IIC_SCL_L();
}

检查过后发现在在SDA转为输出之后,应该先拉低SDA后拉高SCL。题主猜测如果按照上面的程序逻辑,在发起停止信号前,SDA为高电平, 这时候先拉高SCL再拉低SDA,就会再SCL高电平期间实现SDA的下降沿,就产生了起始信号,而反过来就会避免这种情况发生。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面是一个基于STM32 HAL库的模拟I2C驱动OLED显示屏的示例代码。在本示例中,我们使用了STM32F103C8T6开发板和SSD1306 OLED显示屏。 ```c #include "stm32f1xx_hal.h" #define I2C_SCL_Pin GPIO_PIN_6 #define I2C_SCL_GPIO_Port GPIOB #define I2C_SDA_Pin GPIO_PIN_7 #define I2C_SDA_GPIO_Port GPIOB #define OLED_ADDRESS 0x78 void i2c_start(void); void i2c_stop(void); void i2c_send_byte(uint8_t data); void i2c_send_cmd(uint8_t cmd); void i2c_send_data(uint8_t data); void oled_init(void); void oled_clear_screen(void); void oled_write_char(uint8_t x, uint8_t y, uint8_t c); I2C_HandleTypeDef hi2c1; int main(void) { HAL_Init(); __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = I2C_SCL_Pin | I2C_SDA_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); oled_init(); oled_clear_screen(); oled_write_char(0, 0, 'H'); oled_write_char(8, 0, 'e'); oled_write_char(16, 0, 'l'); oled_write_char(24, 0, 'l'); oled_write_char(32, 0, 'o'); while (1); } void i2c_start(void) { HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_RESET); } void i2c_stop(void) { HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_SET); } void i2c_send_byte(uint8_t data) { for (int i = 0; i < 8; i++) { if (data & 0x80) { HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(I2C_SDA_GPIO_Port, I2C_SDA_Pin, GPIO_PIN_RESET); } HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C_SCL_GPIO_Port, I2C_SCL_Pin, GPIO_PIN_RESET); data <<= 1; } } void i2c_send_cmd(uint8_t cmd) { i2c_start(); i2c_send_byte(OLED_ADDRESS << 1); i2c_send_byte(0x00); i2c_send_byte(cmd); i2c_stop(); } void i2c_send_data(uint8_t data) { i2c_start(); i2c_send_byte(OLED_ADDRESS << 1); i2c_send_byte(0x40); i2c_send_byte(data); i2c_stop(); } void oled_init(void) { i2c_send_cmd(0xAE); // 关闭OLED i2c_send_cmd(0xD5); // 设置时钟分频因子,震荡频率 i2c_send_cmd(0x80); // 设置分频因子,震荡频率 i2c_send_cmd(0xA8); // 设置驱动路数 i2c_send_cmd(0x1F); // 默认0x3F(1/64),0x1F(1/32) i2c_send_cmd(0xD3); // 设置显示偏移 i2c_send_cmd(0x00); // 默认为0 i2c_send_cmd(0x40); // 设置显示起始行 i2c_send_cmd(0x8D); // 电荷泵设置 i2c_send_cmd(0x14); // bit2,开启/关闭 i2c_send_cmd(0x20); // 设置内存地址模式 i2c_send_cmd(0x02); // 默认0x02 i2c_send_cmd(0xA0); // 设置段重新映射0->127 i2c_send_cmd(0xC8); // 设置COM扫描方向 i2c_send_cmd(0xDA); // 设置COM硬件引脚配置 i2c_send_cmd(0x12); // bit5,Alternative i2c_send_cmd(0x81); // 对比度设置 i2c_send_cmd(0xEF); // 默认0x7F i2c_send_cmd(0xD9); // 设置预充电周期 i2c_send_cmd(0xF1); // 默认0x22(1.22*Vcc) i2c_send_cmd(0xDB); // 设置VcomH i2c_send_cmd(0x40); // 默认0x20(0.77*Vcc) i2c_send_cmd(0xA4); // 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) i2c_send_cmd(0xA6); // 设置显示方式;bit0:1,反相显示;0,正常显示 i2c_send_cmd(0xAF); // 打开OLED } void oled_clear_screen(void) { for (int i = 0; i < 8; i++) { i2c_send_cmd(0xb0 + i); // 设置页地址(0~7) i2c_send_cmd(0x00); // 设置显示位置—列低地址 i2c_send_cmd(0x10); // 设置显示位置—列高地址 for (int j = 0; j < 128; j++) { i2c_send_data(0x00); // 需要发送的数据 } } } void oled_write_char(uint8_t x, uint8_t y, uint8_t c) { c = c - 32; i2c_send_cmd(0xb0 + y); // 设置页地址(0~7) i2c_send_cmd(((x & 0xf0) >> 4) | 0x10); // 设置显示位置—列高地址 i2c_send_cmd((x & 0x0f) | 0x00); // 设置显示位置—列低地址 for (int i = 0; i < 8; i++) { i2c_send_data(Font8x8[c][i]); } } ``` 在上面的代码中,我们使用了GPIOB的6号和7号引脚分别作为模拟I2C总线的SCL和SDA线,使用了SSD1306 OLED显示屏。在主函数中,我们先初始化OLED,然后清空屏幕并写入一些字符。 在i2c_start()函数中,我们首先将SDA和SCL线都置为高电平,然后将SDA线拉低,接着将SCL线拉低。这样就完成了I2C总线的起始信号。 在i2c_stop()函数中,我们首先将SDA和SCL线都置为低电平,然后将SDA线拉高,接着将SCL线拉高。这样就完成了I2C总线的停止信号。 在i2c_send_byte()函数中,我们先将数据的最高位发送出去,然后依次将数据的其他位发送出去,每发送一位就将SCL线拉高再拉低,这样就完成了一个字节的发送。 在i2c_send_cmd()函数中,我们先发送起始信号,然后发送OLED的地址和写入标志,接着发送寄存器地址和要写入的数据。 在i2c_send_data()函数中,我们先发送起始信号,然后发送OLED的地址和写入标志,接着发送要写入的数据。 在oled_init()函数中,我们依次发送了一些命令,初始化了OLED显示屏。 在oled_clear_screen()函数中,我们先设置页地址,然后设置列地址,接着发送需要写入的数据,这样就可以清空屏幕了。 在oled_write_char()函数中,我们先计算出需要写入的字节的列地址和页地址,然后发送命令和数据,写入字符。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值