STM8S003的GPIO模拟I2C切换输入输出的解决办法

本文介绍了在STM8单片机中,使用GPIO模拟I2C时遇到的从输出切换到输入导致死机的问题。通过研究发现,部分管脚如D4、D5具有开漏特性,可以避免切换过程中的问题。解决方法是设置GPIO为开漏输出,外接上拉电阻,发送数据前先输出高电平,然后读取IO状态以检测ACK响应。文中给出了相关的I2C代码片段。
摘要由CSDN通过智能技术生成

刚开始学STM8,很多东西刚了解,正在做一个温湿度显示小板,就是使用I2C驱动SHT20传感器,并显示到数码管,数码管用的四位一体共阴数码管,STM8管脚有限,添加了一片TM1650做驱动,不过SHT20和TM1650都是用的I2C接口,由于对STM8硬件I2C还不了解,先打算用软件模拟,这就涉及到需要对GPIO做输入输出切换处理来检测ACK响应,刚开始,直接操作DDR寄存器,但是出现从输出切换到输入就会出现单片机死机,不解,查资料,得到一个解决办法,在输出状态时,可以不用切换到输入,我使用管脚D4,D5这两个管脚,这两个管脚比较特殊,就是真正开漏,在管脚描述为T,其它描述为HS(高吸收电流)。


解决办法,将IO设置为开漏输出,外部加上拉,做输入使用之前先输出高,再读取IO状态,即可,附上I2C代码:

#include "swiic.h"

void Delay_us(uint8_t i) //nus 粗略延时
{
    for(; i>0; i--)
    {
        asm("nop");                
        asm("nop");  
        asm("nop");
以下是使用STM8S003作为I2C硬件从机的库函数代码: ``` #include "stm8s.h" // 定义I2C从机地址 #define I2C_SLAVE_ADDRESS 0xA0 // I2C从机接收缓冲区 uint8_t i2c_slave_rx_buf[16]; // I2C从机发送缓冲区 uint8_t i2c_slave_tx_buf[16]; // I2C从机接收数据长度 uint8_t i2c_slave_rx_len = 0; // I2C从机发送数据长度 uint8_t i2c_slave_tx_len = 0; // I2C从机接收完成标志 uint8_t i2c_slave_rx_complete = 0; // I2C从机发送完成标志 uint8_t i2c_slave_tx_complete = 0; // I2C从机错误标志 uint8_t i2c_slave_error = 0; // 初始化I2C从机 void i2c_slave_init(void) { // 初始化GPIOGPIO_Init(GPIOB, GPIO_PIN_4, GPIO_MODE_IN_FL_NO_IT); // SDA GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_IN_FL_NO_IT); // SCL // 初始化I2C外设 I2C_DeInit(); I2C_Init(I2C_SPEED_SM, I2C_SLAVE_ADDRESS, I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, F_CPU); I2C_ITConfig(I2C_IT_EVT | I2C_IT_BUF, ENABLE); } // I2C从机事件处理函数 void i2c_slave_event_handler(void) { if (I2C_GetFlagStatus(I2C_FLAG_SB)) { // 主机发起起始信号 i2c_slave_rx_len = 0; i2c_slave_tx_len = 0; i2c_slave_error = 0; I2C_Send7bitAddress(I2C_SLAVE_ADDRESS, I2C_DIRECTION_RECEIVE); } else if (I2C_GetFlagStatus(I2C_FLAG_ADDR)) { // 主机发送从机地址 if (I2C_GetFlagStatus(I2C_FLAG_TRA)) { // 主机发送写命令 i2c_slave_rx_len = 0; i2c_slave_tx_len = 0; i2c_slave_error = 0; } else { // 主机发送读命令 i2c_slave_rx_len = 0; i2c_slave_tx_len = 16; i2c_slave_error = 0; } I2C_ClearFlag(I2C_FLAG_ADDR); } else if (I2C_GetFlagStatus(I2C_FLAG_RXNE)) { // 主机发送数据 if (i2c_slave_rx_len < 16) { i2c_slave_rx_buf[i2c_slave_rx_len++] = I2C_ReceiveData(); } else { I2C_SendData(0xFF); } I2C_ClearFlag(I2C_FLAG_RXNE); } else if (I2C_GetFlagStatus(I2C_FLAG_TXE)) { // 主机请求数据 if (i2c_slave_tx_len > 0) { I2C_SendData(i2c_slave_tx_buf[i2c_slave_tx_len - 1]); i2c_slave_tx_len--; } else { I2C_SendData(0xFF); } I2C_ClearFlag(I2C_FLAG_TXE); } else if (I2C_GetFlagStatus(I2C_FLAG_STOPF)) { // 主机停止信号 i2c_slave_rx_complete = 1; i2c_slave_tx_complete = 1; I2C_ClearFlag(I2C_FLAG_STOPF); } else if (I2C_GetFlagStatus(I2C_FLAG_BERR)) { // 总线错误 i2c_slave_error = 1; I2C_ClearFlag(I2C_FLAG_BERR); } else if (I2C_GetFlagStatus(I2C_FLAG_ARLO)) { // 总线超时 i2c_slave_error = 1; I2C_ClearFlag(I2C_FLAG_ARLO); } else if (I2C_GetFlagStatus(I2C_FLAG_AF)) { // 应答错误 i2c_slave_error = 1; I2C_ClearFlag(I2C_FLAG_AF); } } // I2C从机中断处理函数 #ifdef _COSMIC_ INTERRUPT_HANDLER(I2C_IRQHandler, ITC_IRQ_I2C) #else void I2C_IRQHandler(void) __interrupt(I2C_IRQ) #endif { i2c_slave_event_handler(); } // 主函数 void main(void) { // 初始化系统时钟 CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 初始化I2C从机 i2c_slave_init(); // 开启全局中断 enableInterrupts(); while (1) { if (i2c_slave_rx_complete) { // I2C从机接收完成 // 处理接收到的数据 i2c_slave_rx_complete = 0; } if (i2c_slave_tx_complete) { // I2C从机发送完成 // 准备发送下一组数据 i2c_slave_tx_complete = 0; } if (i2c_slave_error) { // I2C从机错误 // 处理错误情况 i2c_slave_error = 0; } } } ``` 以上代码仅供参考,具体实现需要根据具体应用场景和硬件配置进行调整和优化。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值