找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1385|回复: 3
打印 上一主题 下一主题
收起左侧

单片机GPIO模拟I2C 写EEPROM延时问题

[复制链接]
跳转到指定楼层
楼主
ID:997026 发表于 2023-7-29 20:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
大家好,

我用GPIO模拟I2C,和外部EEPROM通信,主要是单片机向EEPROM写入数据,写入时规格书要求必须延时5ms.
我现在的程序延时用的是嘀嗒延时,嘀嗒定时器延时应该也是阻塞式延时,我现在要做的就是消除程序里所有的阻塞式延时,其他的延时已经用定时器延改成了非阻塞了,就是这个写EEPROM,我是怎么也没想到比较好的非阻塞延时方式,如果用定时器来延时,没有好的思路。

部分代码如下,
void Write_EEPROM(uint8_t address,uint8_t data,uint8_t paddr) //写数据到EEPROM
{
    IIC_GPIO_Start();
    IIC_WriteOneByte(paddr);
    IIC_WriteOneByte(address);
    IIC_WriteOneByte(data);
    IIC_GPIO_Stop();  
    Systick_delay_ms(5); //此处写完后延时5ms,用的是阻塞式延时,怎么改成非阻塞延时?
}


希望得到各位的帮助,谢谢
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏1 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:883242 发表于 2023-7-31 06:53 | 只看该作者
非阻塞就要把CPU控制权交给其他任务执行,最简单的做法是上RTOS。
回复

使用道具 举报

板凳
ID:1087948 发表于 2023-7-31 12:46 | 只看该作者
你可以使用一个状态机和定时器中断来实现非阻塞延时。在这种情况下,你需要将写EEPROM的过程分解为几个步骤,并为每个步骤定义一个状态。然后,你可以在定时器中断服务程序中改变状态。

以下是一个简单的示例:

c
typedef enum {
    STATE_IDLE,
    STATE_START,
    STATE_WRITE_PADDR,
    STATE_WRITE_ADDRESS,
    STATE_WRITE_DATA,
    STATE_STOP,
    STATE_DELAY
} State;

volatile State state = STATE_IDLE;
uint8_t address, data, paddr;

void TIMx_IRQHandler(void) // 定时器中断服务程序
{
    if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIMx, TIM_IT_Update);
        
        switch (state)
        {
            case STATE_IDLE:
                break;
               
            case STATE_START:
                IIC_GPIO_Start();
                state = STATE_WRITE_PADDR;
                break;
               
            case STATE_WRITE_PADDR:
                IIC_WriteOneByte(paddr);
                state = STATE_WRITE_ADDRESS;
                break;
               
            case STATE_WRITE_ADDRESS:
                IIC_WriteOneByte(address);
                state = STATE_WRITE_DATA;
                break;
               
            case STATE_WRITE_DATA:
                IIC_WriteOneByte(data);
                state = STATE_STOP;
                break;
               
            case STATE_STOP:
                IIC_GPIO_Stop();
                state = STATE_DELAY;
                break;
               
            case STATE_DELAY:
                // 延时5ms后,返回到空闲状态
                static int delayCounter = 0;
                if (++delayCounter >= 5)
                {
                    delayCounter = 0;
                    state = STATE_IDLE;
                }
                break;
        }
    }
}

void Write_EEPROM(uint8_t _address, uint8_t _data, uint8_t _paddr)
{
    // 如果当前状态为空闲,则开始写入过程
    if (state == STATE_IDLE)
    {
        address = _address;
        data = _data;
        paddr = _paddr;
        state = STATE_START;
    }
}
这样,你就可以在主循环中调用Write_EEPROM函数,而不会阻塞其他任务。只有当写入过程完成(即状态返回到STATE_IDLE)时,才能开始新的写入过程。
回复

使用道具 举报

地板
ID:401564 发表于 2023-7-31 12:56 | 只看该作者
你选择了模拟IIC,就注定了效率会变慢的
但这5mS是不需要这样等待的
把等待低电平的部分重新写一个函数
Write_EEPROM只管释放SDA数据线和标志一个标志位,比如IIC_F,然后就函数返回
在主程序中增加一个一个检测IIC_F的函数
进入函数后,先:
if(IIC_F & !SDA)
{
IIC_F = 0;
其它处理;
}
只有收到应答信号之后才进行对应的处理
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表