#define SIMUL 100 /* % PC speed on wokwi.com */
//////////////////////////////////////////////////////////////////////////
/////////////////////// basicthreads.h /////////////////////////
// "basic meets protothreads". Developed by Adam Dunkels & Bill Gates //
// inspired by Donald Knuth ('literate programming'/MIXAL/MAD-Magazine) //
// Bernd vom Berg ("LabVIEW meets µC") //
// Michael H.("zweierkomplement seriel senden?") //
// daniel_duesentrieb ("Wie macht man solche Lichtmodes?") //
// paulpaulson ("sowas hat man noch in der Bibliothek") //
//////////////////////////////////////////////////////////////////////////
//-- basic compatibility ---------------------------------------------- //
#define Loop while(1)
#define For_up_TO(var,from,to) for(var=from; var<=to; var++)
#define For_dn_TO(var,from,to) for(var=from; var>=to; var--)
#define For(var,from,dir,to) For_##dir(var,from,to)
#define Next
#define Function
#define Procedure void
#define then
#define ISR void
#define end_(rem)
#define Exist(pointer) (pointer!=NULL)
#define Print(txt) Serial.print(txt)
#define PrintLn(txt) Serial.println(txt)
// some mixal like /basic compatibility
#define u_byte uint8_t
#define s_byte int8_t
#define u_word uint16_t
#define s_word int16_t
#define eq == /* MIXALl: equal */
#define ne != /* MIXALl: not equal */
#define sh >= /* MIXALl: same or higher */
#define sl <= /* MIXALl: same or lower */
// name'helper'
#define t_conc(name) task__ ##name
#define t_label(name) t_conc(name)
#define t_adr(name) &&t_conc(name)
#if 1
#define t_dbg(txt) Serial.print(txt)
#define t_dbg_ln(txt) Serial.println(txt)
#else
#define t_dbg(txt)
#define t_dbg_ln(txt)
#endif
// for correct use in subs rename 'task' with 'thread' //
// technical requirements: C with gcc-Extension(&& label)
// VAR_tasks(5,int x,y;) use x,y in task__.
#define HEAP_tsk(_max,...) \
const u_byte t_max=_max-1; \
typedef struct \
{void *PC; u_word cs; s_word para; \
__VA_ARGS__;} t_struct; \
static t_struct t_A[_max]; \
static u_byte t_cid; /*current*/ \
static s_byte t_tid=-128;/*tmp id */ \
static u_word centis; /*time */
#define t_cS String(t_cid)
#define t_tS String(t_tid)
// ________ variable 'frame' _________
#define iT_(id,var) t_A[id].var
#define T_(var) iT_(t_cid,var) /*cur !!!*/
#define T_S(var) String(T_(var))
#define iT_S(var) String(iT_(id,var))
// break: save PC+1 return
#define t_store_PC(pc) T_(PC) = t_adr(pc)
#define t_break(pc) t_store_PC(pc); goto t_brk_vec
#define t_Break {t_break(__LINE__); t_label(__LINE__):/* come back*/;}
// call task
#define t_call(adr) {goto *(adr); t_brk_vec:;}
// task_container{...}_____________________________________________.
#define tsk_container /* run all one step */ \
For(t_cid, 0, up_TO, t_max) if Exist(T_(PC)) then t_call(T_(PC)); \
if(t_tid eq -2)/* prevent execution*/
// task_intit{autostarts} ___________________________________.
#define tsk_init for(;t_tid==-128;t_tid=(t_tid==-128)?0:t_tid)
// setTask(id,name,parameter) __________________________________.
#define setTask(uid,name,p){t_dbg("set:"#name" u:"+String(uid));\
if((0 sl uid) and (uid sl t_max)) \
then { iT_(uid,PC)=t_adr(name); iT_(uid,para)=p; \
t_dbg_ln(" OK"); } \
else t_dbg_ln(" out of range"); \
end_(setTask);}
// newTask(name,parameter)______________________________________.
#define newTask(name,p){ t_dbg("new:"#name" u:");; \
For(t_tid,t_max,dn_TO,0) \
if (iT_(t_tid,PC) eq NULL) {setTask(t_tid,name,p); break;}\
if(t_tid < 0) t_dbg_ln(" to much tasks");\
end_(newTask);}
// t_callTask(T_(A),test,NULL)_use in TASK______.
#define t_callTask(pid,name,_para) \
{ newTask(name,_para); pid=t_tid; \
if(pid sh 0) then t_Wait(!Exist(iT_(pid, PC)))}
// stopTask(3) _______________________________________.
#define freeTask(ind){t_dbg_ln("free id:"+String(ind));\
iT_(ind,PC)=NULL; }
// freeTasks(-1) _____________________________________________.
#define freeTasks(leave){ t_dbg_ln("free all"+String(leave)); \
For(t_tid,0,up_TO,t_max leave) iT_(t_tid,PC)=NULL; }
//________ time sharing _________________________
#define t_Yield t_Break /* syntax compatiblity to scheduled task */
#define t_Wait(cond) while(!(cond)) t_Yield ;
#define t_Wait_cs(_cs) { T_(cs) = centis + _cs; \
t_Wait(centis eq T_(cs));}
#define t_Until(cond) t_Yield; if(cond) break;
#define t_Switch_to(name){ T_(PC)=t_adr(name);goto t_brk_vec;}
#define t_End { t_dbg_ln("end: id:"+t_cS); \
T_(PC)=NULL; goto t_brk_vec;}
// _________ task 'frame' _______________________________
#define TASK(name) t_conc(name): \
t_dbg_ln("run:"#name" id:" + t_cS+" p:"+ T_S(para)); \
Loop{
#define task_until(cond) t_Until(cond); }; t_End;
#define task_next t_Yield; };
#define task_end t_End; };
#define task_goto(name) t_Switch_to(name); };
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#define pinsCount 12
u_byte pin_sort_[] = {2,3,4,5,6,7,8,9,10,11,12,13};
u_byte pin_sort[] = {13,12,11,10,9,8,7,6,5,4,3,2};
u_byte pins_cur[pinsCount]; u_byte pins_des[pinsCount];
bool pins_cur_[pinsCount];
bool flag_100hz;
enum {w_dn,w_fade,w_fix};
Procedure wr_pin(u_byte num,u_byte val,u_byte sw){
switch(sw){
case w_dn: pins_des[num-1]=000; pins_cur[num-1]=val;break;
case w_fade:pins_des[num-1]=val; break;
case w_fix: pins_des[num-1]=val; pins_cur[num-1]=val;break;
end_(sw);}
end_(wr_pin);}
Procedure w_pin_(u_byte num, u_byte val, s_byte sw){
pins_des[num-1]=0; pins_cur[num-1]=val;
end_(w_pin);}
ISR simul_100hz_upd(){int i;
flag_100hz=true;
For(i,0,up_TO,pinsCount-1){
if(pins_cur[i]!=pins_des[i])
if(pins_cur[i]>pins_des[i]) pins_cur[i]--;
else pins_cur[i]++;
digitalWrite(pin_sort[i], pins_cur[i]);
// analogWrite(pin_sort[i],pins_cur[i]);
Next;}
end_(TSR);}
//---------- some buttons ----------//
#define btn_dbg A5
#define btn1 A2
#define btn2 A0
enum { btn_press=1*256,btn_repeat=4*256,
btn_short=2*256,btn_long =3*256};
u_byte btn_scan;
Function u_word button(){
u_word tmp_return;
static u_byte time;
u_byte scan;
scan=1*(digitalRead(btn1)==LOW)+2*(digitalRead(btn2)==LOW);
if(scan ne btn_scan)
{ if(time<42) tmp_return=btn_scan+btn_short;
else tmp_return=btn_scan+btn_long;
btn_scan=scan; time=0; }
else switch(time++) {
case 1:tmp_return=scan + btn_press; break;
case 60:tmp_return=scan + btn_repeat;break;
case 100:time=59;
default:tmp_return=0; }
return tmp_return;}
//////////////////////////////////////////////////////////////////////////
#define ana1 A1
#include <TimerOne.h>
Procedure setup() {int i;
For (i,0,up_TO,pinsCount-1) pinMode(pin_sort[i], OUTPUT);
pinMode(btn1, INPUT_PULLUP); pinMode(btn2, INPUT_PULLUP);
pinMode(ana1, INPUT);
Serial.begin(38400); // 10ms (expected)
Timer1.initialize((((long)10*1000 *SIMUL/100)));
Timer1.attachInterrupt(simul_100hz_upd);
end_(setup);}
#define test_pin(num,duration) \
{wr_pin(num,25,w_dn); t_Wait_cs(duration); };
Procedure loop() {
HEAP_tsk(8,int A,B,C);
if(flag_100hz){flag_100hz=false; centis++;}
// else return;//'slow' task only
//----------------------------------------------------------------------
tsk_container{
TASK (blink)
wr_pin(12,30,w_dn);
t_Wait_cs(T_(para));
task_until(0)
TASK (on)
For(T_(A),1,up_TO,pinsCount-1){
wr_pin(T_(A),200,w_dn);
t_Wait_cs(30);
Next;}
task_goto(lr_1_12)
TASK (lr_1_12)
For(T_(A),1,up_TO,pinsCount-1)
test_pin(T_(A),T_(para));
task_next
TASK (kn)
For(T_(A),-7,up_TO,6) test_pin(abs(T_(A))+1,T_(para));
task_next
TASK (kn_1_8)
For(T_(A),1,up_TO,8) test_pin(T_(A),T_(para));
task_goto( kn_7_2 )
TASK (kn_7_2)
For(T_(A),7,dn_TO,2) test_pin(T_(A),T_(para));
task_goto( kn_1_8 )
TASK (prg_1)
t_callTask(T_(A),flash,80);t_dbg_ln("T_(A): tmp");
t_Wait_cs(500); t_dbg_ln("5 sec");
t_callTask(T_(A),flash,40);
task_goto(kn);
TASK (prg_2)
task_end
TASK (flash)
For(T_(A),1,up_TO,5) wr_pin(T_(A),50,w_dn);
t_Wait_cs(50);
For(T_(A),5,up_TO,9) wr_pin(T_(A),50,w_dn);
task_end
TASK (btn)
switch (button()){
case 1+btn_press : PrintLn("btn 1-Press free ");freeTask(1); break;
case 1+btn_repeat: PrintLn("btn 1-Rep flash 1 ");setTask(1,flash,40);break;
case 1+btn_short : PrintLn("btn 1-shrt kn slow ");setTask(1, kn_1_8 ,60);break;
case 1+btn_long : PrintLn("btn 1-lng kn fast ");setTask(1, kn_7_2 ,30);break;
case 2+btn_press : PrintLn("btn 2-Press "); break;
case 2+btn_repeat: PrintLn("btn 2-Rep "); break;
case 2+btn_short : PrintLn("btn 2-shrt add max ");newTask( lr_1_12 ,50);break;
case 2+btn_long : PrintLn("btn 2-lng stp all -3");freeTasks(-3); break;
end_(sw_button)}
t_Wait_cs(1);
task_next
TASK( slide)//T_t(var) typical last used id but also used by stop
static byte ana_val; byte _ana_val;
_ana_val=(1023-analogRead(ana1))/10+10;
if(ana_val and (ana_val ne _ana_val) and (t_tid sh 0))
{ iT_(t_tid,para)=ana_val; PrintLn("slide:"+String(ana_val)+" id:"+t_tS);}
ana_val=_ana_val;
task_next;
end_(container);}
//----------------------------------------------------------------------
tsk_init{ t_dbg_ln("init");
newTask( slide, NULL);
newTask( btn, NULL);
newTask( blink, 100);
setTask(-1, lr_1_12, 20);//-1: some kind of don't start
setTask( 1, kn, 73);
setTask( 1, prg_1, 73);
end_(init);}
end_(loop);}
// in memoriam: Sven Nehlsen (DGzRS, DJ, ... )