找回密碼
 立即注冊(cè)

QQ登錄

只需一步,快速開(kāi)始

搜索
查看: 980|回復(fù): 3
打印 上一主題 下一主題
收起左側(cè)

一種非阻塞式函數(shù)框架實(shí)現(xiàn)方法思路

[復(fù)制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
單片機(jī)程序中,如果沒(méi)有上RTOS,遇到延時(shí)delay和while時(shí),往往程序就阻塞在該處,造成除了中斷之外的函數(shù)無(wú)法執(zhí)行。
為了解決這個(gè)問(wèn)題,我想到了一種一種非阻塞式的函數(shù)框架,既然delay和while造成的,我們就想辦法繞開(kāi)它。
先說(shuō)delay的實(shí)現(xiàn)

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
//軟件定時(shí)器結(jié)構(gòu)
typedef struct
{
    uint32_t SetValue;
    uint32_t Target_Ticks;
    uint8_t Run_Step;
} Dtimer_Typedef;
//設(shè)置軟件定時(shí)器的延時(shí)值函數(shù),這個(gè)目的是在其他地方可以修改更新延時(shí)值
void dtimer_set(Dtimer_Typedef *dtimer, uint32_t delayvalue);     
//設(shè)置軟件定時(shí)器延時(shí)值并開(kāi)始計(jì)時(shí)函數(shù)
void dtimer_en(Dtimer_Typedef *dtimer, uint32_t delayvalue);   
//設(shè)置軟件定時(shí)器復(fù)位函數(shù)
void dtimer_reset(Dtimer_Typedef *dtimer);   
//判斷軟件定時(shí)器是否剛到(到定時(shí)值時(shí)第一次會(huì)返回true,第2次返回false,相當(dāng)于上升沿,只執(zhí)行一次)
bool dtimer_reaching(Dtimer_Typedef *dtimer);   
//判斷軟件定時(shí)器是否已到(已到定時(shí)值時(shí),如果沒(méi)有復(fù)位會(huì)一直返回true)
bool dtimer_reached(Dtimer_Typedef *dtimer);
#endif

dtimer.c文件   
#include "main.h"
#include "dtimer.h"
//設(shè)置軟件定時(shí)器的延時(shí)值函數(shù),這個(gè)目的是在其他地方可以修改更新延時(shí)值
void dtimer_set(Dtimer_Typedef *dtimer, uint32_t delayvalue)
{
   
if ((*dtimer).Run_Step != 1)
    {
        (*dtimer).SetValue = delayvalue;
    }
}
//設(shè)置軟件定時(shí)器延時(shí)值并開(kāi)始計(jì)時(shí)函數(shù),定時(shí)值到后,必須dtimer_reset后才會(huì)再次執(zhí)行
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;
    }
}
//設(shè)置軟件定時(shí)器復(fù)位,復(fù)位后,遇到dtimer_en才會(huì)啟用軟件定時(shí)器
void dtimer_reset(Dtimer_Typedef *dtimer)
{
   
if ((*dtimer).Run_Step != 0)
    {
        (*dtimer).Run_Step =
0;
    }
}
//判斷軟件定時(shí)器是否剛到(到定時(shí)值時(shí)第一次會(huì)返回true,第2次返回false,相當(dāng)于上升沿,只執(zhí)行一次)
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;
    }
}
//判斷軟件定時(shí)器是否已到(已到定時(shí)值時(shí),如果沒(méi)有復(fù)位會(huì)一直返回true)
bool dtimer_reached(Dtimer_Typedef *dtimer)
{
   
if (millis() > (*dtimer).Target_Ticks)
    {
        
return true;
    }
   
else
    {
        
return false;
    }
}

文件中millis() 為 其他c文件實(shí)現(xiàn)的系統(tǒng)毫秒計(jì)時(shí)返回值,類(lèi)似于Arduino的中millis()作用,其他單片機(jī)可以用定時(shí)中斷計(jì)數(shù)累加,如
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)    //定時(shí)器1毫秒中斷調(diào)用
{
      MILLIS_CNT++;
      TEMPMS_CNT++;
      
if(TEMPMS_CNT==60)
      {
          SECOND_CNT++;
          TEMPMS_CNT =
0;
      }
}
uint32_t seconds(
void)   //獲取系統(tǒng)運(yùn)行當(dāng)前秒
{
   
return SECOND_CNT;
}
uint32_t millis()  
//獲取系統(tǒng)運(yùn)行當(dāng)前毫秒
{
  
return MILLIS_CNT;
}
void delay(uint32_t time)  //傳統(tǒng)的阻塞式毫秒延時(shí)函數(shù)
{
    uint32_t m_times=millis()+time;
   
while(millis()<m_times);
}
void delayus(uint32_t time)  //傳統(tǒng)的阻塞式微秒延時(shí)函數(shù)
{
    uint32_t m_times=micros()+time;
   
while(micros()<m_times);
}
uint32_t micros(
void)     //獲取系統(tǒng)運(yùn)行當(dāng)前微秒
{
    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
一般的方法:延時(shí)過(guò)程,其他代碼被阻塞,無(wú)法執(zhí)行
int main(void)
{
   
while(1)
    {
         P01=
1;
         delay(
100);
         P01=
0;
         delay(
300);
         其它代碼....
    }
}

非阻塞的方法:延時(shí)過(guò)程,
其他代碼正常執(zhí)行,但P01的延時(shí)效果跟傳統(tǒng)一樣
#include "dtimer.h"
static Dtimer_Typedef DT[2]
int main(void)
{
   
while(1)
    {
         dtimer_en(&DT[
0],100);    //軟件定時(shí)器0啟用,計(jì)時(shí)100毫秒
         if(dtimer_reaching(&DT[0]))    //軟件定時(shí)器0延時(shí)到,執(zhí)行一次
         {
             P01=
0;    //P01滅
             dtimer_reset(&DT[1]);   //復(fù)位軟件定時(shí)器1,避免第二次執(zhí)行時(shí),下面的dtimer_en不起作用
             dtimer_en(&DT[1],300); //軟件定時(shí)器1啟用,計(jì)時(shí)300毫秒
         }  
         
else  //軟件定時(shí)器0延時(shí)未到
         {
              P01=
1; //P01亮
         }
         
if(dtimer_reaching(&DT[1]))   //軟件定時(shí)器1延時(shí)到,執(zhí)行一次
         {            
              dtimer_reset(&DT[
0]);  //復(fù)位軟件定時(shí)器0
         }  
         其他代碼...
     }
}

while的常規(guī)代碼,遇到fun_1 while時(shí),fun_2就會(huì)因?yàn)樽枞麤](méi)有運(yùn)行到
如:
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的非阻塞式轉(zhuǎn)換,fun_1()和傳統(tǒng)while一樣,i<100時(shí),會(huì)不斷執(zhí)行條件里面的代碼2.. ,但此時(shí)fun_2()并沒(méi)有被阻塞
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
        ...
}

不知道大家有沒(méi)有其他不一樣的思路,一起討論下
以下是我調(diào)試的畫(huà)面截圖,整個(gè)過(guò)程運(yùn)行正常


分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏7 分享淘帖 頂 踩
回復(fù)

使用道具 舉報(bào)

沙發(fā)
ID:857072 發(fā)表于 2024-9-27 18:20 來(lái)自手機(jī) | 只看該作者
可以了解一下protothreads協(xié)程。結(jié)構(gòu)簡(jiǎn)單消耗也小。關(guān)鍵是調(diào)用結(jié)構(gòu)就是一般的函數(shù)結(jié)構(gòu)容易接受
回復(fù)

使用道具 舉報(bào)

板凳
ID:471574 發(fā)表于 2024-9-28 05:46 來(lái)自手機(jī) | 只看該作者
a185980800 發(fā)表于 2024-9-27 18:20
可以了解一下protothreads協(xié)程。結(jié)構(gòu)簡(jiǎn)單消耗也小。關(guān)鍵是調(diào)用結(jié)構(gòu)就是一般的函數(shù)結(jié)構(gòu)容易接受

protothreads也算RTOS吧
回復(fù)

使用道具 舉報(bào)

地板
ID:857072 發(fā)表于 2024-9-28 11:47 來(lái)自手機(jī) | 只看該作者
不能上算rtos,頂多算個(gè)任務(wù)管理框架吧
回復(fù)

使用道具 舉報(bào)

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表