找回密码
 立即注册

QQ登录

只需一步,快速开始

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

一种非阻塞式函数框架实现方法思路

[复制链接]
跳转到指定楼层
楼主
单片机程序中,如果没有上RTOS,遇到延时delay和while时,往往程序就阻塞在该处,造成除了中断之外的函数无法执行。
为了解决这个问题,我想到了一种一种非阻塞式的函数框架,既然delay和while造成的,我们就想办法绕开它。
先说delay的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243


dtimer.h文件
#ifndef _DTIMER_H
#define _DTIMER_H
//软件定时器结构
typedef struct
{
    uint32_t SetValue;
    uint32_t Target_Ticks;
    uint8_t Run_Step;
} Dtimer_Typedef;
//设置软件定时器的延时值函数,这个目的是在其他地方可以修改更新延时值
void dtimer_set(Dtimer_Typedef *dtimer, uint32_t delayvalue);     
//设置软件定时器延时值并开始计时函数
void dtimer_en(Dtimer_Typedef *dtimer, uint32_t delayvalue);   
//设置软件定时器复位函数
void dtimer_reset(Dtimer_Typedef *dtimer);   
//判断软件定时器是否刚到(到定时值时第一次会返回true,第2次返回false,相当于上升沿,只执行一次)
bool dtimer_reaching(Dtimer_Typedef *dtimer);   
//判断软件定时器是否已到(已到定时值时,如果没有复位会一直返回true)
bool dtimer_reached(Dtimer_Typedef *dtimer);
#endif

dtimer.c文件   
#include "main.h"
#include "dtimer.h"
//设置软件定时器的延时值函数,这个目的是在其他地方可以修改更新延时值
void dtimer_set(Dtimer_Typedef *dtimer, uint32_t delayvalue)
{
   
if ((*dtimer).Run_Step != 1)
    {
        (*dtimer).SetValue = delayvalue;
    }
}
//设置软件定时器延时值并开始计时函数,定时值到后,必须dtimer_reset后才会再次执行
void dtimer_en(Dtimer_Typedef *dtimer, uint32_t delayvalue)     
{
   
if ((*dtimer).Run_Step == 0)
    {
        
if(delayvalue>0)
        {
            (*dtimer).SetValue = delayvalue;
        }
        (*dtimer).Target_Ticks = millis() + (*dtimer).SetValue;
        (*dtimer).Run_Step =
1;
    }
}
//设置软件定时器复位,复位后,遇到dtimer_en才会启用软件定时器
void dtimer_reset(Dtimer_Typedef *dtimer)
{
   
if ((*dtimer).Run_Step != 0)
    {
        (*dtimer).Run_Step =
0;
    }
}
//判断软件定时器是否刚到(到定时值时第一次会返回true,第2次返回false,相当于上升沿,只执行一次)
bool dtimer_reaching(Dtimer_Typedef *dtimer)
{
   
if ((*dtimer).Run_Step == 1)
    {
        
if (millis() > (*dtimer).Target_Ticks)
        {
            (*dtimer).Run_Step =
2;
            
return true;
        }
        
else
        {
            
return false;
        }
    }
   
else
    {
        
return false;
    }
}
//判断软件定时器是否已到(已到定时值时,如果没有复位会一直返回true)
bool dtimer_reached(Dtimer_Typedef *dtimer)
{
   
if (millis() > (*dtimer).Target_Ticks)
    {
        
return true;
    }
   
else
    {
        
return false;
    }
}

文件中millis() 为 其他c文件实现的系统毫秒计时返回值,类似于Arduino的中millis()作用,其他单片机可以用定时中断计数累加,如
volatile
static uint32_t MILLIS_CNT = 0;
volatile
static uint32_t TEMPMS_CNT = 0;
volatile
static uint32_t SECOND_CNT = 0;

void SysTick_Handler(void)    //定时器1毫秒中断调用
{
      MILLIS_CNT++;
      TEMPMS_CNT++;
      
if(TEMPMS_CNT==60)
      {
          SECOND_CNT++;
          TEMPMS_CNT =
0;
      }
}
uint32_t seconds(
void)   //获取系统运行当前秒
{
   
return SECOND_CNT;
}
uint32_t millis()  
//获取系统运行当前毫秒
{
  
return MILLIS_CNT;
}
void delay(uint32_t time)  //传统的阻塞式毫秒延时函数
{
    uint32_t m_times=millis()+time;
   
while(millis()<m_times);
}
void delayus(uint32_t time)  //传统的阻塞式微秒延时函数
{
    uint32_t m_times=micros()+time;
   
while(micros()<m_times);
}
uint32_t micros(
void)     //获取系统运行当前微秒
{
    uint32_t ms = millis();
    uint32_t systick_value = SysTick->VAL;
    uint32_t systick_load = SysTick->LOAD +
1;
    uint32_t us = ((systick_load - systick_value) *
1000) / systick_load;
   
return (ms * 1000 + us);
}

使用方法:比如P01 高电平-亮100ms,然后低电平-灭300ms
一般的方法:延时过程,其他代码被阻塞,无法执行
int main(void)
{
   
while(1)
    {
         P01=
1;
         delay(
100);
         P01=
0;
         delay(
300);
         其它代码....
    }
}

非阻塞的方法:延时过程,
其他代码正常执行,但P01的延时效果跟传统一样
#include "dtimer.h"
static Dtimer_Typedef DT[2]
int main(void)
{
   
while(1)
    {
         dtimer_en(&DT[
0],100);    //软件定时器0启用,计时100毫秒
         if(dtimer_reaching(&DT[0]))    //软件定时器0延时到,执行一次
         {
             P01=
0;    //P01灭
             dtimer_reset(&DT[1]);   //复位软件定时器1,避免第二次执行时,下面的dtimer_en不起作用
             dtimer_en(&DT[1],300); //软件定时器1启用,计时300毫秒
         }  
         
else  //软件定时器0延时未到
         {
              P01=
1; //P01亮
         }
         
if(dtimer_reaching(&DT[1]))   //软件定时器1延时到,执行一次
         {            
              dtimer_reset(&DT[
0]);  //复位软件定时器0
         }  
         其他代码...
     }
}

while的常规代码,遇到fun_1 while时,fun_2就会因为阻塞没有运行到
如:
int i=0;
int main(void)
{
   
while(1)
    {  
        fun_1();
        fun_2();
    }
}
void fun_1()
{
        代码1
        ...
        
while(i<100)
        {
             代码2
             ...
             i++;
        }
        代码3
        ...
}
void fun_2()
{
        代码4
        ...
}
while的非阻塞式转换,fun_1()和传统while一样,i<100时,会不断执行条件里面的代码2.. ,但此时fun_2()并没有被阻塞
int i=0;
static index=0;
int main(void)
{
   
while(1)
    {  
        fun_1();
        fun_2();
    }
}
void fun_1()
{      
      
switch(index)
       {
        
case 0:
           代码1
            ...
            index=
1;
            
break;
        
case 1:
            
if(i<100)
              {            
                 代码2
                  ...
                 i++;
               }
               
else
               {
                   index=
2;
               }
               
break;
         
case 3:
             代码3
              ...
              index=
0;
              
break;
         }
}
void fun_2()
{
        代码4
        ...
}

不知道大家有没有其他不一样的思路,一起讨论下
以下是我调试的画面截图,整个过程运行正常


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏7 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:857072 发表于 2024-9-27 18:20 来自手机 | 只看该作者
可以了解一下protothreads协程。结构简单消耗也小。关键是调用结构就是一般的函数结构容易接受
回复

使用道具 举报

板凳
ID:471574 发表于 2024-9-28 05:46 来自手机 | 只看该作者
a185980800 发表于 2024-9-27 18:20
可以了解一下protothreads协程。结构简单消耗也小。关键是调用结构就是一般的函数结构容易接受

protothreads也算RTOS吧
回复

使用道具 举报

地板
ID:857072 发表于 2024-9-28 11:47 来自手机 | 只看该作者
不能上算rtos,顶多算个任务管理框架吧
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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