单片机硬件和软件延时、RTOS相对延时和绝对延时_freertos延时对串口发送的影响

已剪辑自: https://mp.weixin.qq.com/s/-RPLQn4KO9Aqu1fpfZeOKA

前不久有个读者在问关于延时的问题,大概就是问:软件延时和硬件延时是啥意思?做项目时他俩有什么区别?
今天就来讲讲关于硬件延时和软件延时的内容,以及它们的区别。

延时的种类很多,先给大家普及一下延时相关概念和分类。

指利用具有计数功能的硬件进行延时。

比如:定时器(Timer)、 实时时钟(RTC)、 系统滴答定时器(SysTick)等具有计数功能的硬件。

相对硬件延时而言,软件延时就是写一段软件代码,通过消耗CPU时间进行延时。

比如软件延时函数:

void Delay(uint32_t Cnt){  uint32_t i;
  while(Cnt--)  {    for(i=0; i<0x80000; i++);  }}

实际应用中,延时分阻塞和非阻塞延时。

指CPU一直停留阻塞,不去做其它事情,直到延时结束结束。

像上面那个软件延时(Delay)就是一个典型的阻塞延时,一直消耗CPU,直到延时结束。

指在延时期间,没有阻塞CPU,也就是说CPU在延时期间可以执行其它代码。

比如:利用定时器中断延时,只需要开启定时器,在中断(计数)到来之前,CPU可以执行其它代码。

a.利用定时器也能实现阻塞延时,比如STM32的HAL自带的阻塞延时:

__weak void HAL_Delay(uint32_t Delay){  uint32_t tickstart = HAL_GetTick();  uint32_t wait = Delay;
  /* Add a freq to guarantee minimum wait */  if (wait < HAL_MAX_DELAY)  {    wait += (uint32_t)(uwTickFreq);  }
  while ((HAL_GetTick() - tickstart) < wait)  {  }}

b.利用RTOS自带的系统延时实现非阻塞延时,这个实现原理实际是利用了硬件延时(系统滴答定时器)。

当然,这个延时的原理(延时函数代码)相对比较复杂,对于普通用户只需要知道如何调用以及简单原理即可,感兴趣的老铁可以自行研究一下。

通常在一些RTOS的(Demo)例子的任务中都有系统延时,比如ucos非阻塞延时:

OSTimeDly(10);

图片

再比如FreeRTOS非阻塞延时:

vTaskDelay(10);

图片

关于RTOS的延时,这里额外拓展一下关于RTOS中的相对延时和绝对延时的内容,请移步至文章《RTOS中相对延时和绝对延时的区别

通过以上分析,其实不难得出,硬件延时相对软件延时更普遍。

1.软件相对硬件延时精度更差;

2.软件延时为阻塞延时,硬件延时可阻塞,也可非阻赛延时;

3.硬件延时应用更灵活、更广泛;


已剪辑自: https://mp.weixin.qq.com/s?__biz=MzI4MDI4MDE5Ng==&mid=2247513149&idx=2&sn=90586ddd99a0871745e5e57c3a6b1cfb&chksm=ebb819d6dccf90c008aeb35e6de7eb24f9ec39c8d7ae27b8613f89822a626f942fb8d3482c3d&scene=21#wechat_redirect

嵌入式软件代码中延时是很常见的,只是延时种类有很多,看你用什么延时。

**问题:**周期性(固定一个时间)去处理某一件事情。你会通过什么方式去实现?

比如:间隔10ms去采集传感器的数据,然后通过一种算法计算出一个结果,最后通过串口发送出去。

可能对于很多习惯裸机编程的读者,首先想到的是:利用定时器,定时10ms中断,在中断里面处理。
中断函数适合处理简单数据,不适合算法、通信等需要长时间占用CPU的处理。
对计时精度要求比较高的地方适合定时器,像本章节说的周期性采集传感器数据,要求不适合很高,那么就引入本文说的绝对延时。
在实时操作系统FreeRTOS任务中,利用vTaskDelayUntil绝对延时即可完美解决这个问题。

本文拿FreeRTOS中相对延时函数vTaskDelay,绝对延时函数vTaskDelayUntil来说明。

**相对延时:**指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间(参数:滴答值)结束。

**绝对延时:**指每隔指定的时间(参数:滴答值),执行一次调用vTaskDelayUntil()函数的任务。

文字描述可能不够直观理解,下面章节结合代码例子、延时值(IO高低变化波形)、任务执行图来详细讲述一下他们的区别。

<think>嗯,用户问的是在单片机中使用IPS显示屏放在主函数循环里是否需要延时。这个问题看起来是关于单片机编程显示屏驱动的结合应用。首先,我需要回忆一下单片机驱动显示屏的一般方法,特别是IPS屏幕的特性。 首先,IPS显示屏通常是通过SPI或者I2C这样的串行接口与单片机通信的。这些接口的通信速率有一定的限制,而且显示屏本身在处理指令数据时需要一定的时间。如果主循环中没有适当的延时,可能会导致数据发送过快,超过显示屏的处理能力,从而出现显示错误或者数据丢失的情况。 接下来,要考虑主循环的结构。主循环通常是一个无限循环,不断执行显示更新其他任务。如果在每次更新显示时立即发送数据,而没有给显示屏足够的时间处理,可能会导致通信冲突。例如,SPI接口虽然全双工,但主设备需要等待从设备准备好才能继续传输。这时候可能需要检查忙标志或者插入适当的延时。 然后,关于延时的类型。有些显示屏有忙标志(Busy Flag),可以通过读取该标志来确定是否可以发送下一条指令。这种方式比固定的延时更高效,因为可以避免不必要的等待。但如果显示屏不支持忙标志查询,就必须依靠固定的延时时间,这需要根据数据手册中的时序要求来设置。 另外,还要考虑实时性的问题。如果主循环中还有其他任务需要处理,长时间的延时可能会影响整个系统的响应速度。这时候可能需要采用非阻塞的延时方法,比如使用定时器中断来管理时序,或者在主循环中分步骤处理显示更新,避免一次性占用太多时间。 还有一点是,不同的驱动库或底层实现可能已经处理了延时的问题。例如,使用Adafruit或U8g2这样的库时,库函数内部可能会自动插入必要的延时或等待忙标志。这时候用户在主循环中不需要额外添加延时,只需要按照库的规范调用函数即可。 不过,如果用户是自己编写底层驱动,那么必须严格按照显示屏的数据手册中的时序要求来添加延时。例如,发送复位命令后可能需要几毫秒的等待时间,或者每条指令之间需要几微秒的间隔。这些延时通常通过__nop()指令、循环计数或者调用系统的延时函数来实现。 还需要考虑显示屏的刷新率。高刷新率虽然显示效果更流畅,但也会增加单片机的负担。如果主循环的迭代速度超过显示屏的刷新能力,可能会导致部分帧被跳过,或者显示内容混乱。因此,适当控制主循环的执行频率,或者使用垂直同步之类的机制,可能会更有效。 总结一下,是否需要延时取决于多个因素:显示屏的通信协议、是否支持忙标志查询、驱动库的实现方式、主循环中其他任务的耗时,以及所需的刷新率等。通常来说,在直接操作硬件的情况下,适当的延时是必要的,但最好通过查询状态或使用非阻塞延时来优化性能。</think>在单片机中使用IPS显示屏时,主函数循环中是否需要延时,取决于以下关键因素: **需要延时的典型场景:** 1. **通信协议时序要求** - 通过SPI/I2C等接口通信时,需遵守数据手册规定的指令间隔时间(如`0x11`复位指令后需5ms延时) - 示例代码: ```c send_command(0x11); // 发送复位命令 delay_ms(5); // 必须等待5ms ``` 2. **无忙标志(Busy Flag)检测** - 当显示屏不支持状态查询时,必须使用固定延时: ```c void update_display() { write_data(buffer); // 写入显示数据 delay_us(50); // 等待数据传输完成 } ``` 3. **防止数据覆盖** - 连续刷新时需保证帧完整: ```c while(1) { render_frame(); // 渲染新帧 refresh_display(); // 刷屏操作 delay_ms(16); // 约60Hz刷新率(1000/60 ≈ 16.67ms) } ``` **无需延时的情况:** 1. **使用硬件流控制** - 当支持DMA传输或硬件握手信号时(如SPI的`READY`引脚),可通过中断驱动实现零等待 2. **状态查询优化** - 优先采用忙标志检测(更高效): ```c void send_cmd(uint8_t cmd) { while(check_busy_pin() == 1); // 等待显示屏就绪 SPI_transfer(cmd); } ``` 3. **分时处理策略** - 在RTOS或多任务系统中,可通过任务调度替代延时: ```c void display_task() { while(1) { update_display(); vTaskDelay(pdMS_TO_TICKS(10)); // FreeRTOS延时 } } ``` **最佳实践建议:** 1. **时序验证** 使用逻辑分析仪测量实际通信波形,确保`tWRESET`、`tCAS`等参数符合手册要求 2. **动态延时调整** 根据温度/电压变化实现自适应延时: ```c uint16_t dynamic_delay = calibrate_delay(); // 上电自校准 delay_us(dynamic_delay); ``` 3. **功耗优化** 在电池供电场景中,可结合低功耗模式: ```c enter_sleep_mode(); // 进入休眠 wake_up_on_vsync(); // 通过垂直同步中断唤醒 ``` 典型显示控制流程示例: ```c while(1) { static uint32_t last_refresh = 0; if(HAL_GetTick() - last_refresh >= REFRESH_INTERVAL) { /* 非阻塞式刷新 */ if(!display_is_busy()) { // 检查状态 refresh_frame_buffer(); last_refresh = HAL_GetTick(); } } handle_user_input(); // 并行处理其他任务 } ``` 结论:多数情况下需要合理延时,但应优先采用状态检测、硬件流控制等高效方法,延时时间需严格参照具体显示屏型号的数据手册(如ST7789、ILI9341等不同驱动IC要求差异较大)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值