Navigation
Sequental realtime
A realtime krnl
Generic examples
Debugging short
Internal breakout functions
Source
Non gnu on AVR
Interrupts
Litt
Getting krnl
Other kernels
|
KRNL - An Arduino KernelDownload - always github I have found it interesting to develop an open source realtime kernel (KRNL) for the Arduino platform:
Some characteristics:
See templates for a number of commented runable examples which illustrates very well different mockups. Access to codewelcome history
git clone https://github.com/jdn-aau/krnl.git
krnl consists of two files krnl.h and krnl.cpp which you shall save in directory sketchbook/libraries/krnl or click here - beware wuite many lines of code /******************************************************* * * * * * | |/ /___| _ \| \ | | ___| | * * | ' // _ \ |_) | \| |/ _ \ | * * | . \ __/ _ <| |\ | __/ |___ * * |_|\_\___|_| \_\_| \_|\___|_____| * * * * * * you are watching krnl.c * * * * March 2015,2016,..,2018 * * Author: jdn * * final sep 2022 * * * ******************************************************* * 2022 .. * this version adapted for Arduino * * (C) 2012,2013,2014 * 2017,2018,2019,2021,2022 * * * IF YOU ARE LUCKY LOOK HERE * * https://github.com/jdn-aau/krnl * * * * Jens Dalsgaard Nielsen <jdn@es.aau.dk> * http://es.aau.dk/staff/jdn * Section of Automation & Control * Aalborg University, * Denmark * * "THE BEER-WARE LICENSE" (frit efter PHK) * <jdn@es.aau.dk> wrote this file. As long as you * retain this notice you can do whatever you want * with this stuff. If we meet some day, and you think * this stuff is worth it ... * you can buy me a beer in return :-) * or if you are real happy then ... * single malt will be well received :-) * * Use it at your own risk - no warranty * * nice info... * http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus * at 20090611 * - k_eat_time now eats time in quants of krnl tick speed but not one quant *boundary * - added k_malloc and k_free * k_free dont free memory bq we do not want fragmentation * so DONT USE k_free * - k_malloc and k_free are weak functions so you can just add your own *versions * - watchdog is enabled (2sec and reset in krnl timer) in weak function *k_enable_wdt * * remember to update in krnl.c !!! *****************************************************/ #define KRNL_VRS 20221027 #ifndef KRNL #define KRNL // KRNLBUG ACTIVATE BREAK OUT FUNCTIONS // Look in krnl.c lines 13xx #define KRNLBUG // BACKSTOPPER wraps a looping fct around your task so it will just restart // like loop function // BEWARE bq local variables in the task body just evaporate - as always #define BACKSTOPPER // IF YOU WANT READER WRITER LOCK THEN DEFINE #define READERWRITER // Define watchdog timer and period // if you enable it it will be running at once //#define WDT_TIMER #define WDT_PERIOD WDTO_1S // if you want to use k_malloc // NB k_free wont release mem due to possible fragmentation // SO DONT USE k_free its just a fake // NEVER !!! free men in a rt system... #define DYNMEMORY #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) \ || defined(__AVR_ATmega2561__) #define KRNLTMR 2 #elif defined(__AVR_ATmega1284P__) #define KRNLTMR 2 #elif defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || defined(__AVR_ATmega328__) #define KRNLTMR 2 #elif defined(__AVR_ATmega32U4__) #define KRNLTMR 3 #else #error "unknown AVR cpu type - bad place to come" #endif /*********************** NB NB ABOUT WRAP AROUND Krnl maintain a milisecond timer (k_millis_counter) It s 32 bit unsigned long so it wraps around after 49.7 days. As all timing internal in krnl is relative (from now) then wrap around will have no influence on krnl !!!fk_eat NB NB ABOUT TIMERS PORTS ETC You can configure krnl to use timer 1,2,3,4,5 If you want to use timer 0 then you need to - set KRNLTMR = 0 just below in USER CONFIGURATION PART - mangle with Arduino library code in ... hardware/arduino/avr/cores/arduino/wiring.c rename ISR ... with function head: #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ISR(TIM0_OVF_vect) #else ISR(TIMER0_OVF_vect) #endif to something else : mayby void kurt() This is because krnl install an ISR on timer0 and there cant be two Krnl to maintain the millis counters so everything with millis, micros etc is as usual. Buth normally you cant use timer 0 bq it is used for millis and preallocated. See below When using a timer you must be aware of that it will prohibit you from things like - tone (pwm sound) uses timer2 ... from http://blog.oscarliang.net/arduino-timer-and-interrupt-tutorial/ Timer0: - 8bit timer. - In the Arduino world Timer0 is been used for the timer functions, like delay(), millis() and micros(). - If you change Timer0 registers, this may influence the Arduino timer function. - So you should know what you are doing. - 16bit timer. - In the Arduino world the Servo library uses Timer1 on Arduino Uno (Timer5 on Arduino Mega). Timer2: - 8bit timer like Timer0. - In the Arduino world the tone() function uses Timer2. Timer3 16 bits - 1280/1284P and 2560 only Timer4, Timer5 16 bits - 1280 and 2560 only On Uno - Pins 5 and 6: controlled by timer0 - Pins 9 and 10: controlled by timer1 - Pins 11 and 3: controlled by timer2 On the Arduino Mega we have 6 timers and 15 PWM outputs: TODO pinout below need checkup - Pins 4 and 13: controlled by timer0 - Pins 11 and 12: controlled by timer1 - Pins 9 and10: controlled by timer2 - Pin 2, 3 and 5: controlled by timer 3 - Pin 6, 7 and 8: controlled by timer 4 - Pin 46, 45 and 44:: controlled by timer 5 ... from http://arduino-info.wikispGitte Merete Holm-Pedersenaces.com/Timers-Arduino - Servo Library uses Timer1. -- You can’t use PWM on Pin 9, 10 when you use the Servo Library on an Arduino. -- For Arduino Mega it is a bit more difficult. The timer needed depends on the number of servos. -- Each timer can handle 12 servos. -- For the first 12 servos timer 5 will be used (losing PWM on Pin 44,45,46). -- For 24 Servos timer 5 and 1 will be used (losing PWM on Pin 11,12,44,45,46).. -- For 36 servos timer 5, 1 and 3 will be used (losing PWM on Pin 2,3,5,11,12,44,45,46).. -- For 48 servos all 16bit timers 5,1,3 and 4 will be used (losing all PWM pins). - Pin 11 has shared functionality PWM and MOSI. -- MOSI is needed for the SPI interface, You can’t use PWM on Pin 11 and the SPI interface at the same time on Arduino. -- On the Arduino Mega the SPI pins are on different pins. - tone() function uses at least timer2. -- You can’t use PWM on Pin 3,11 when you use the tone() function an Arduino and Pin 9,10 on Arduino Mega. SO BEWARE !!! PERFORMANCE std internal speed 1 kHz Rudimentary prog with one task the timer ISR takes about 21 usec Uno, leonardo and mega measures the same period: timer0 1024 usec for 1 msec ... timer1,2,3... 1007 usec for 1 msec >>>>>>>>>>>> MODIFY OF/hardware/arduino/avr/cores/arduino/wiring.c I have added the MOD part (MOD: modification) So if you go for timer0 1)remove the // in front of #define MOD 2) change here in krnl.h KRNLTMR to 0 for your architecture ONLY SUPPORT FOR AVR's (uno,leonardo,nano,mega,...) //#define MOD #if defined (MOD) void justFakeFctHead() #else #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) ISR(TIM0_OVF_vect) 769769876987 lalalalalalal #else ISR(TIMER0_OVF_vect) #endif #endif >>>>>>>>>>>> ***********************************************************/ #ifndef sbi #define sbi(r, b) r |= _BV(b) #endif #ifndef cbi #define cbi(r, b) r &= ~_BV(b) #endif #ifndef rbi #define cbi(r, b) r &= ~_BV(b) #endif // >>>>>>>>>>>>>>>>> USER CONFIGURATION PART <<<<<<<<<<<<<<<<<< #define QHD_PRIO 102 // Queue head prio - for sentinel use #define ZOMBI_PRIO (QHD_PRIO - 1) #define DMY_PRIO (QHD_PRIO - 2) // dummy task prio (0 == highest prio) #define DMY_STK_SZ 90 // staksize for dummy #define MAIN_PRIO 50 // main task prio #define STAK_HASH 0x5c // just a hashcode #define MAX_SEM_VAL 50 // NB is also max for nr elem in msgQ ! #define MAX_INT 0x7FFF // not in use #define SEM_MAX_DEFAULT 50 #define SEM_MAX_VALUE 10000 #define CEILINGFAILPRIO -4 #define CEILINGFAILNOTCEIL -3 /* which timer to use for krnl heartbeat timer 0 ( 8 bit) is normally used by millis - avoid ! timer 1 (16 bit) timer 2 ( 8 bit) not 32u4 timer 3 (16 bit) 32u4/1280/1284p/2560 only timer 4 (16 bit) 1280/2560 only (MEGA) timer 5 (16 bit) 1280/2560 only (MEGA) */ // END USER CONFIGURATION // check for legal timers #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) || \ defined(__AVR_ATmega328__) #if (KRNLTMR != 0) && (KRNLTMR != 1) && (KRNLTMR != 2) #error "bad timer selection for krnl heartbeat(168/328/328p/...)" #endif #endif #if defined(__AVR_ATmega32U4__) #if (KRNLTMR != 0) && (KRNLTMR != 1) && (KRNLTMR != 3) #error "bad timer selection for krnl heartbeat(32u4)...)" #endif #endif #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || \ defined(__AVR_ATmega2561__) #if (KRNLTMR != 0) && (KRNLTMR != 1) && (KRNLTMR != 2) && (KRNLTMR != 3) && \ (KRNLTMR != 4) && (KRNLTMR != 5) #error "bad timer for krnl heartbeat(1280/2560/2561) in krnl" #endif #endif #if defined(__AVR_ATmega1284P__) #if (KRNLTMR != 0) && (KRNLTMR != 1) && (KRNLTMR != 2) && (KRNLTMR != 3) #error "bad timer for krnl heartbeat(1284P) in krnl" #endif #endif //---------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif extern int k_task, k_sem, k_msg; extern volatile char krnl_preempt_flag; extern char dmy_stk[DMY_STK_SZ]; /***** KeRNeL data types *****/ struct k_t { #ifdef BACKSTOPPER void (*pt)(void); #endif unsigned char nr; struct k_t *next, // task,sem: double chain lists ptr *pred; // task,sem: double chain lists ptr volatile char sp_lo, // sem:vacant | task: low 8 byte of stak adr sp_hi; // sem: vacant |task: high 8 byte of stak adr char prio, // task & sem: priority ceiling_prio, // sem saved_prio; // semaohore volatile int cnt1, // sem: sem counter | task: ptr to stak cnt2, // asem: dyn part of time counter | task: timeout cnt3, // sem: preset timer value | task: ptr to Q we are hanging in maxv, // sem: max value | task: org priority clip; // sem: counter for lost signals | task: vacant }; extern unsigned char k_coopFlag; struct k_msg_t { // msg type unsigned char nr; struct k_t *sem; char *pBuf; // ptr to user supplied ringbuffer volatile int nr_el, el_size, lost_msg; volatile int r, w, cnt; }; #ifdef READERWRITER struct k_rwlock_t { int nrReaders; struct k_t *rdwrSem, *rdSem, *fifoSem; }; #endif /***** KeRNeL variables *****/ extern volatile char k_wdt_enabled; extern struct k_t *task_pool, *sem_pool, AQ, // activeQ *pmain_el, *pAQ, *pDmy, // ptr to dummy task descriptor *pRun, // ptr to running task *pSleepSem; extern struct k_msg_t *send_pool; extern char nr_task, nr_sem, nr_send; extern volatile char k_running; // no running extern volatile char k_err_cnt; // every time an error occurs cnt is incr by one extern unsigned long k_millis_counter; extern char k_preempt_flag; /****************************************************** MACROS MACROS PUSHREGS AND POPREGS is actual staklayout plus task address at top A push/pop takes 2 cycles a call takes 3 cycles ret/iret 3-4 cycles So a PUSHREGS is 33 instructions(@ 2 cycles) = 66 cycles ~= 66 cycles /"16 MHz" ~= 4.1 usec So an empty iSR which preserves all registers takes 2*4.1usec + 8-10 cycles (intr + iret) ~= 9 usec So max isr speed with all regs saved is around 100 kHz but then with no code inside ISR ! WARNING The 2560 series has 3 bytes PC the rest only 2 bytes PC !!! (PC. program counter) and no tall has rampz and eind register REGISTER NAMING AND INTERNAL ADRESSING https://en.wikipedia.org/wiki/Atmel_AVR_instruction_set Register I/O address Data address SREG 0x3F 0x5F SP 0x3E:0x3D 0x5E:0x5D EIND 0x3C 0x5C RAMPZ 0x3B 0x5B RAMPY 0x3A 0x5A RAMPX 0x39 0x59 RAMPD 0x38 0x58 A typical ATmega memory map may look like: Data address I/O address Contents 0x0000 – 0x001F Registers R0 – R31 general purpose regs 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 a 10 b 11 c 12 d 13 e 14 f 15 10 16 11 17 12 18 13 19 14 20 15 21 16 22 17 23 18 24 19 25 1a 26 Xref L 1b 27 Xreg H 1c 28 Yreg L 1d 29 Yreg H 1e 30 Zreg L 1f 31 Zreg H 0x0020 – 0x003F 0x00 – 0x1F I/O registers (bit-addressable) 0x0040 – 0x005F 0x20 – 0x3F I/O registers (not bit-addressable) 0x0060 – 0x00FF Extended I/O registers (memory-mapped I/O only) 0x0100 – RAMEND Internal SRAM Register addresses IO adr data addr 0x3f SREG 0x5f 0x3e SPH 0x3d SPL 0x3c EIND 0x5c 1280/2560 only 0x3b RAMPZ 0x5b 1280/2560/1284p only ... 0x1f R31 etc 0x01 R1 0x00 R0 PC is NOT available */ #define lo8(X) ((unsigned char)((unsigned int)(X))) #define hi8(X) ((unsigned char)((unsigned int)(X) >> 8)) extern volatile char k_bug_on; #ifdef KRNLBUG #define K_F_CHG_STAK() \ if (pRun != AQ.next) { \ pRun->sp_lo = SPL; \ pRun->sp_hi = SPH; \ pRun = AQ.next; \ k_breakout(); \ SPL = pRun->sp_lo; \ SPH = pRun->sp_hi; \ } #define K_CHG_STAK() \ if (!k_coopFlag) { \ if (pRun != AQ.next) { \ pRun->sp_lo = SPL; \ pRun->sp_hi = SPH; \ pRun = AQ.next; \ k_breakout(); \ SPL = pRun->sp_lo; \ SPH = pRun->sp_hi; \ } \ } #else #define K_F_CHG_STAK() \ if (pRun != AQ.next) { \ pRun->sp_lo = SPL; \ pRun->sp_hi = SPH; \ pRun = AQ.next; \ SPL = pRun->sp_lo; \ SPH = pRun->sp_hi; \ } #define K_CHG_STAK() \ if (!k_coopFlag) { \ if (pRun != AQ.next) { \ pRun->sp_lo = SPL; \ pRun->sp_hi = SPH; \ pRun = AQ.next; \ SPL = pRun->sp_lo; \ SPH = pRun->sp_hi; \ } \ } #endif // MISSING no code 1284p /* below: r1 must/shall always assumed to be zero in c code (gcc issue I think) */ // AVR / ARDUINO PART PUSH POP // 0x3b RAMPZ extended z-pointer register // 0x3c EIND extended indirect register #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) || \ defined(__AVR_ATmega2561__) || defined(__AVR_ATmega1284P__) || \ defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || \ defined(__AVR_ATmega328__) || defined(__AVR_ATmega32U4__) || \ defined(__AVR_ATmega328PB__) #define DI() __asm__ volatile("cli") #define EI() __asm__ volatile("sei") #define RETI() __asm__ volatile("reti") #endif /* below: r1 must/shall always assumed to be zero in c code (gcc issue I think) */ #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) || \ defined(__AVR_ATmega2561__) // 0x3b RAMPZ extended z-pointer register // 0x3c EIND extended indirect register #define PUSHREGS() \ __asm__ volatile("push r1 \n\t" \ "push r0 \n\t" \ "in r0, __SREG__ \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "in r0 , 0x3b \n\t" \ "push r0 \n\t" \ "in r0 , 0x3c \n\t" \ "push r0 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \ "push r6 \n\t" \ "push r7 \n\t" \ "push r8 \n\t" \ "push r9 \n\t" \ "push r10 \n\t" \ "push r11 \n\t" \ "push r12 \n\t" \ "push r13 \n\t" \ "push r14 \n\t" \ "push r15 \n\t" \ "push r16 \n\t" \ "push r17 \n\t" \ "push r18 \n\t" \ "push r19 \n\t" \ "push r20 \n\t" \ "push r21 \n\t" \ "push r22 \n\t" \ "push r23 \n\t" \ "push r24 \n\t" \ "push r25 \n\t" \ "push r26 \n\t" \ "push r27 \n\t" \ "push r28 \n\t" \ "push r29 \n\t" \ "push r30 \n\t" \ "push r31 \n\t") #define POPREGS() \ __asm__ volatile("pop r31 \n\t" \ "pop r30 \n\t" \ "pop r29 \n\t" \ "pop r28 \n\t" \ "pop r27 \n\t" \ "pop r26 \n\t" \ "pop r25 \n\t" \ "pop r24 \n\t" \ "pop r23 \n\t" \ "pop r22 \n\t" \ "pop r21 \n\t" \ "pop r20 \n\t" \ "pop r19 \n\t" \ "pop r18 \n\t" \ "pop r17 \n\t" \ "pop r16 \n\t" \ "pop r15 \n\t" \ "pop r14 \n\t" \ "pop r13 \n\t" \ "pop r12 \n\t" \ "pop r11 \n\t" \ "pop r10 \n\t" \ "pop r9 \n\t" \ "pop r8 \n\t" \ "pop r7 \n\t" \ "pop r6 \n\t" \ "pop r5 \n\t" \ "pop r4 \n\t" \ "pop r3 \n\t" \ "pop r2 \n\t" \ "pop r0 \n\t" \ "out 0x3c , r0 \n\t " \ "pop r0 \n\t" \ "out 0x3b , r0 \n\t " \ "pop r0 \n\t" \ "out __SREG__ , r0 \n\t " \ "pop r0 \n\t" \ "pop r1 \n\t") #elif defined(__AVR_ATmega1284P__) // 0x3b RAMPZ extended z-pointer register // 0x3c EIND extended indirect register #define PUSHREGS() \ __asm__ volatile("push r1 \n\t" \ "push r0 \n\t" \ "in r0, __SREG__ \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "in r0 , 0x3b \n\t" \ "push r0 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \ "push r6 \n\t" \ "push r7 \n\t" \ "push r8 \n\t" \ "push r9 \n\t" \ "push r10 \n\t" \ "push r11 \n\t" \ "push r12 \n\t" \ "push r13 \n\t" \ "push r14 \n\t" \ "push r15 \n\t" \ "push r16 \n\t" \ "push r17 \n\t" \ "push r18 \n\t" \ "push r19 \n\t" \ "push r20 \n\t" \ "push r21 \n\t" \ "push r22 \n\t" \ "push r23 \n\t" \ "push r24 \n\t" \ "push r25 \n\t" \ "push r26 \n\t" \ "push r27 \n\t" \ "push r28 \n\t" \ "push r29 \n\t" \ "push r30 \n\t" \ "push r31 \n\t") #define POPREGS() \ __asm__ volatile("pop r31 \n\t" \ "pop r30 \n\t" \ "pop r29 \n\t" \ "pop r28 \n\t" \ "pop r27 \n\t" \ "pop r26 \n\t" \ "pop r25 \n\t" \ "pop r24 \n\t" \ "pop r23 \n\t" \ "pop r22 \n\t" \ "pop r21 \n\t" \ "pop r20 \n\t" \ "pop r19 \n\t" \ "pop r18 \n\t" \ "pop r17 \n\t" \ "pop r16 \n\t" \ "pop r15 \n\t" \ "pop r14 \n\t" \ "pop r13 \n\t" \ "pop r12 \n\t" \ "pop r11 \n\t" \ "pop r10 \n\t" \ "pop r9 \n\t" \ "pop r8 \n\t" \ "pop r7 \n\t" \ "pop r6 \n\t" \ "pop r5 \n\t" \ "pop r4 \n\t" \ "pop r3 \n\t" \ "pop r2 \n\t" \ "pop r0 \n\t" \ "out 0x3b , r0 \n\t " \ "pop r0 \n\t" \ "out __SREG__ , r0 \n\t " \ "pop r0 \n\t" \ "pop r1 \n\t") #elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328PB__) \ || defined(__AVR_ATmega328__) || defined(__AVR_ATmega32U4__) #define PUSHREGS() \ __asm__ volatile("push r1 \n\t" \ "push r0 \n\t" \ "in r0, __SREG__ \n\t" \ "cli \n\t" \ "push r0 \n\t" \ "clr r1 \n\t" \ "push r2 \n\t" \ "push r3 \n\t" \ "push r4 \n\t" \ "push r5 \n\t" \ "push r6 \n\t" \ "push r7 \n\t" \ "push r8 \n\t" \ "push r9 \n\t" \ "push r10 \n\t" \ "push r11 \n\t" \ "push r12 \n\t" \ "push r13 \n\t" \ "push r14 \n\t" \ "push r15 \n\t" \ "push r16 \n\t" \ "push r17 \n\t" \ "push r18 \n\t" \ "push r19 \n\t" \ "push r20 \n\t" \ "push r21 \n\t" \ "push r22 \n\t" \ "push r23 \n\t" \ "push r24 \n\t" \ "push r25 \n\t" \ "push r26 \n\t" \ "push r27 \n\t" \ "push r28 \n\t" \ "push r29 \n\t" \ "push r30 \n\t" \ "push r31 \n\t") #define POPREGS() \ __asm__ volatile("pop r31 \n\t" \ "pop r30 \n\t" \ "pop r29 \n\t" \ "pop r28 \n\t" \ "pop r27 \n\t" \ "pop r26 \n\t" \ "pop r25 \n\t" \ "pop r24 \n\t" \ "pop r23 \n\t" \ "pop r22 \n\t" \ "pop r21 \n\t" \ "pop r20 \n\t" \ "pop r19 \n\t" \ "pop r18 \n\t" \ "pop r17 \n\t" \ "pop r16 \n\t" \ "pop r15 \n\t" \ "pop r14 \n\t" \ "pop r13 \n\t" \ "pop r12 \n\t" \ "pop r11 \n\t" \ "pop r10 \n\t" \ "pop r9 \n\t" \ "pop r8 \n\t" \ "pop r7 \n\t" \ "pop r6 \n\t" \ "pop r5 \n\t" \ "pop r4 \n\t" \ "pop r3 \n\t" \ "pop r2 \n\t" \ "pop r0 \n\t" \ "out __SREG__ , r0 \n\t " \ "pop r0 \n\t" \ "pop r1 \n\t") #else #error "unknown arch" #endif #define d8t13 \ for (int i = 8; i < 14; i++) { \ pinMode(i, OUTPUT); \ digitalWrite(i, LOW); \ } #define kbreakout \ extern "C" { \ void k_breakout() { PORTB = (1 << pRun->nr); } \ } // function prototypes // naming convention // k_... function do a DI/EI and can impose task shift // ki_... expects interrupt to be disablet and do no task shift // rest is internal functions #ifdef DYNMEMORY /** call malloc protected by DI and EI if dft as weak so you can supply with your own */ void *k_malloc(int k); /** call free empty - no function if dft as weak so you can supply with your own @remark do no free mem bq we do not want fragmented memory */ void k_free(void *m); #endif /** * Call wdt_enable (AVR lib) * DI(); * wdt_enable(WDT_PERIOD); * EI(); * WDT_PERIOD is in KRNL i sec * @param[t] watchdog timout WDT_PERIOD is 1 sec * See avrlib docu for more info * The watchdog is reset in krnls timer ISR when active */ void __attribute__((weak)) k_wdt_enable(int t); /** * disable wdt * Disable the watchdog timer */ void __attribute__((weak)) k_wdt_disable(); /** Returns nr of milliseconds since krnl was started by k_start @return: unsigned long is returned @remark only to be called after start of KRNL and only when interrupt is disabled @remark To be called within an ISR so interrupts is no disabled */ unsigned long ki_millis(void); /** Returns nr of milliseconds since krnl was started by k_start @return: unsigned long is returned @remark only to be called after start of KRNL @remark Interrupt is disabled when reading krnl time and is enabled afterwards */ unsigned long k_millis(void); /** * Eat time in quant of krnl ticks * starting from now but not from next tick * Eats CPU time in k_tick quants @param[in] tyick number of ticks to eat */ void k_eat_msec(unsigned int eatTime); /** issues a task shift - handle with care Not to be used by normal user */ void ki_task_shift(void) __attribute__((naked)); /** Set task asleep for a number of ticks. @param[in] time nr of ticks to sleep < 0 @return: 0 is ok(has been suspended), 1(is not suspended), -1 is wait time is less than 0 @remark only to be called after start of KRNL */ int k_sleep(int time); /** creates a task and put it in the active Q @param[in] pTask pointer to function for code ( void task(void) ...) @param[in] stkSize size of data area(in bytes) to be used for stak @param[in] prio - task prio max: 1 lowest < DUMMY_PRIO @return: pointer to task handle or NULL if no success @remark only to be called before start of KRNL but after k_init */ struct k_t *k_crt_task(void (*pTask)(void), char prio, char *pS, int stkSize); /** change priority of calling task) @param[in] prio new prio, Priority 1: highest (QHEAD_PRIO-1): lowest @return: 0: ok, -1: KRNL not running, -2: illegal value @remark only to be called after start of KRNL */ int k_set_prio(char prio); /** creates a standard Dijkstra semaphore. It can be initialized to values in range [0..maxvalue] @param[in] init_val startvalue of semaphore 0,1,2,... maxvalue @param[in] maxvalue which is maxvalue semaphore can reach @return handle to semaphore or NULL pointer @remark only to be called before start of KRNL */ struct k_t *k_crt_sem(int init_val, int maxvalue); /** adds ceiling priority to semaphore use k_mut_ceil_enter k_mut_ceil_leave instead of wait and signal Can only be called before k_start @param[in] sem reference to sem used for mutex - no check @param[in] prio Ceiling pirority @return 0: ok , otherwise bad bad @remark only to be called before start of KRNL */ /** creates a standard mutex. It can be initialized to values in range [0..maxvalue] @param[in] init_val startvalue of semaphore 0,1,2,... maxvalue @param[in] maxvalue which is maxvalue semaphore can reach @return handle to semaphore or NULL pointer @remark only to be called before start of KRNL */ struct k_t *k_crt_mut(int ceiling_prio, int init_val, int maxvalue); int k_mut_ceil_set(struct k_t *sem, char prio); /** Enter mutex (eq to k_wait...) use k_mut_enter / k_mut_leave instead of wait and signal @param[in] sem aka mutex @param[in] timeout timeout value @return 0: if you have been waiting, 1 if you just went through, -1 for timeout, -3 if your priority was higher than ceiling priority */ int k_mut_ceil_enter(struct k_t *sem, int timeout); /** Leave mutex (eq to k_signal...) use k_mut_enter/ k_mut_leave instead of wait and signal @param[in] sem used as mutex @return 0: ok otherwise bad bad */ int k_mut_ceil_leave(struct k_t *sem); /** Mutex enter and leave w imm priority ceiling in one operation @param sm mutex with ceiling @param timeout 0 == forever @fct void fct(void) the function w critical code @return 0: ok ,waited, 1 straight through; -1 timeout, -3 CEILINGFAILNOTCEIL not a semaphore w ceiling, -4 CEILINGFAIL my priority is above ceil max prio */ int k_mut_ceil(struct k_t *sem, int timeout, void (*fct)(void)); /** attach a timer to the semaphore so KRNL will signal the semaphore with regular intervals. Can be used for cyclic real time run of a task. @param[in] sem semaphore handle @param[in] val interval in quant of KRNL ticks (0: disable cyclic timer, 1,2,3... cyclic quant) @return -1: negative val, 0. ok @remark only to be called after start of KRNL */ int k_set_sem_timer(struct k_t *sem, int val); /** Signal a semaphore. Can be called from an ISR when interrupt is disabled. No task shift will occur - only queue manipulation. @param[in] sem semaphore handle @return 0: ok , -1: max value of semaphore reached @remark only to be called after start of KRNL */ int ki_signal(struct k_t *sem); /** Signal a semaphore. Task shift will task place if a task is started by the signal and has higher priority than you. @param[in] sem semaphore handle @return 0: ok , -1: max value of semaphore reached @remark The ki_ indicates that interrups is NOT enabled when leaving ki_signal @remark only to be called after start of KRNL */ int k_signal(struct k_t *sem); /** Wait on a semaphore. Task shift will task place if you are blocked. @param[in] sem semaphore handle @param[in] timeout "<0" you will be started after timeout ticks, "=0" wait forever "-1" you will not wait @return 1: ok there was a signal hanging - so no suspension @return 0: ok- you have been suspended @return -1: timeout has occured, -2 no wait bq timeout was -1 and semaphore was negative @remark only to be called after start of KRNL */ int k_wait(struct k_t *sem, int timeout); /** Wait on a semaphore. Task shift will task place if you are blocked. @param[in] sem semaphore handle @param[in] timeout "<0" you will be started after timeout ticks, "=0" wait forever "-1" you will not wait @param[out] nrClip If not NULL you will get in return nr of clip on signals on sem and clip counter will be set to 0 @return 1: ok there was a signal hanging - so no suspension @return 0: ok- you have been suspended @return -1: timeout has occured, -2 no wait bq timeout was -1 and semaphore was negative @remark only to be called after start of KRNL */ int k_wait2(struct k_t *sem, int timeout, int *nrClip); /** Returns how many signals has been lost on semaphore due to saturation @param[in] sem semaphore handle @return nr of signals lost @remark only to be called after start of KRNL */ int k_sem_signals_lost(struct k_t *sem); /** Like k_wait with the exception interrupt is NOT enabled when leaving @param[in] sem semaphore handle @param[in] timeout "<0" you will be started after timeout ticks, "=0" wait forever "-1" you will not wait @return 0: ok , -1: could do wait bw blocking would have taken place @remark The ki_ indicates that interrups is NOT enabled when leaving ki_wait @remark only to be called after start of KRNL @remark normally not to be used by users !!! BEWARE */ int ki_wait(struct k_t *sem, int timeout); /** returns value of semaphore @param[in] sem semaphore handle @return 1: ok not suspended, 0: ok you have been suspended @return -1 no wait maybe bq no timeout was allowed @remark only to be called after start of KRNL @remark do not enable interrupt upon leaving */ int ki_semval(struct k_t *sem); /** returns value of semaphore @param[in] sem semaphore handle @return 1: ok not suspended, 0: ok you have been suspended @return -1 no wait maybe bq no timeout was allowed @remark only to be called after start of KRNL */ int k_semval(struct k_t *sem); /** * Clear semaphore if possible. * Will set semaphore vallue 0 if possible (if no tasks is waiting) * @param[in] sem pointer ref to semaphore * @return semValue - if negative it indicates task are waiting * */ int k_clear_sem(struct k_t *sem); /** returns nr of pending messages @param[in] msgbuffer handle xxx */ int ki_msg_count(struct k_msg_t *msgbuffer); /** returns nr of pending messages @param[in] msgbuffer handle @return 1: ok not suspended, 0: ok you have been suspended @return -1 no wait maybe bq no timeout was allowed @remark only to be called after start of KRNL */ int k_msg_count(struct k_msg_t *m); /********************************************************************* ************************* DEBUG BREAKOUT FUNCTIONS ******************* ********************************************************************** * * Breakout functions can be removed by commenting out define of * KRNLBUG in krnl.h (located around line 216) * All semaphore calls k_sem_clip, k_sem_signal, k_Sem_wait are all called * AFTER decrement/incr of semaphore has taken place BUT before eventually * taskshift. * * For message buffers/semaphores an internal semaphore is used for synchronization * so referring to the msgQ is by * struct k_msg_t *msgSem; msgSem->sem->nr * */ /** a function for overloading on usersite which is called when a semaphore is overflooding no occur - it's only reading out semaphore idendity Signal operations has not taken place ! 1: means first semahore allocated by user, 2 the next etc Interrupt is disabled when called and must not be enabled during.. so no print etc @param[in] nr id of semaphore 1,2,3,... @param[in] nrClip number of times clip has occured (may be reset by call k_wait_lost) */ #ifdef KRNLBUG void __attribute__((weak)) k_sem_clip(unsigned char nr, int nrClip); #endif /** * called in a signal call val is sem val AFTER signal has taken place * @param [out] nr number of semaphore. Semaphores are allocated by user buth * also in message queues. So it may be diff to find the semaphore... * */ #ifdef KRNLBUG void __attribute__((weak)) k_sem_signal(unsigned char nr, int val); #endif /** * called when wait called. val is sem value after countdown has taken place * NB is the wait call willl not wait and there is no token at the semaphore * val will be -1111 */ #ifdef KRNLBUG void __attribute__((weak)) k_sem_wait(unsigned char nr, int val); #endif /** a function for overloading on usersite which is called when a msgQ is overflooding no reset occur - it's only readind out smsgQ idendity 1: means first msgQ allocated by user etc Interrupt is disabled when called and must not be enabled during.. so no print etc @param [out] nr : id of send Q 0,1,2,... @param [out] nrClip: number of times clip has occured (may be reset by call k_receive and lost parm not eq NULL) */ #ifdef KRNLBUG void __attribute__((weak)) k_send_Q_clip(unsigned char nr, int nrClip); #endif struct k_msg_t *k_crt_send_Q(int nr_el, int el_size, void *pBuf); /** Put data (one element of el_size)in the ringbuffer if there are room for it. Intended for ISR use DONE BY COPY ! @param[in] pB Ref to message buffer @param[in] el Reference to data to be put in buffer. Size if already given in k_crt_send @return 0: operation did succed, -1: no room in ringbuffer @remark Interrupt will not enabled upon leaving, so ki_send is intended to be used from an ISR @remark only to be called before start of KRNL */ char ki_send(struct k_msg_t *pB, void *el); /** Put data (one element of el_size)in the ringbuffer if there are room for it. DONE BY COPY ! @param[in] pB Ref to message buffer @param [in] el Reference to data to be put in buffer. Size if already given in k_crt_send @return 0: operation did succed, -1: no room in ringbuffer @remark only to be called after start of KRNL @remark k_send does not block if no space in buffer. Instead -1 is returned */ char k_send(struct k_msg_t *pB, void *el); /** Receive data (one element of el_size)in the ringbuffer if there are data DONE BY COPY ! @param[in] pB Ref to message buffer @param [out] el Reference to where data shall be copied to at your site @param[in] timeout Max time you want to wait on data, -1: never, 0: forever, positive: nr of KRNL timer quants @param[out] lost_msg nr of lost messages since last receive. will clip at 10000. If lost_msg ptr is NULL then overrun counter is not reset to 0. @return 1: ok no suspension, 0: operation did succed, -1: no data in ringbuffer @remark only to be called after start of KRNL */ char k_receive(struct k_msg_t *pB, void *el, int timeout, int *lost_msg); /** Receive data (one element of el_size)in the ringbuffer if there are data DONE BY COPY ! No blocking if no data Interrupt will not be enabled after ki_receive and intr must be blocked prior to call @param[in] pB Ref to message buffer @param[out] el Reference to where data shall be copied to at your site @param[out] lost_msg nr of lost messages since last receive. will clip at 10000. If lost_msg ptr is NULL then overrun counter is not reset to 0 @return 1: data was rdy no suspension, 0: ok you have been suspended , -1: no data in ringbuffer @remark can be used from ISR @remark only to be called after start of KRNL */ char ki_receive(struct k_msg_t *pB, void *el, int *lost_msg); /** * Empty sendQ * @return given no of deleted messages * @return -1 if its no a msg q * @return -2 if krnl is not running * @return 0 or positive gives no of messages deleted */ int ki_clear_msg_Q(struct k_msg_t *pB); /** * Empty sendQ * See ki_clear_msg-Q for description */ int k_clear_msg_Q(struct k_msg_t *pB); #ifdef READERWRITER /** initialise a reader-writers comples */ void k_rwInit(struct k_rwlock_t *lock); /** reader-writer Read enter */ int k_rwRdEnter(struct k_rwlock_t *lock, int timeout); /** reader-writer Write enter */ int k_rwWrEnter(struct k_rwlock_t *lock, int timeout); /** reader-writer Read leave */ int k_rwRdLeave(struct k_rwlock_t *lock); /** reader-writer Write leave */ int k_rwWrLeave(struct k_rwlock_t *lock); #endif /** returns which timer is used @return 0,1,2,3,4,5 ... */ int k_tmrInfo(void); // tm in milliseconds /** Initialise KRNL. First function to be called. You have to give max number of tasks, semaphores and message queues you will use @param[in] nrTask ... @param[in] nrSem ... @param[in] nrMsg ... */ int k_init(int nrTask, int nrSem, int nrMsg); /** Starts krnl is possible If there has been ciritcal errors between k_init and k_start (like no more task descriptors) k_start will return a negative number indicatin how many errors If k_start starts you will not return from k_start */ int k_start(void); // tm in milliseconds /** stop KRNL If krnl is not yet running you will return with a -1 as return value If krnl is running interrupt will be disabled and k_stop ends in an infinite loop */ int k_stop(); // tm in milliseconds /** Reset by disable interrupt plus activate watchdog (15 millisseconds) and just wait... **/ void k_reset(); /** Initialise blink on pin 13 ON when dummy is running Nov 2014 - fake do not use it bq it will not work for emergency use :-) */ /** returns nr of unbytes bytes on stak. For chekking if stak is too big or to small... @param[in] t Reference to task (by task handle) If null then yourself @return: nr of unused bytes on stak (int) @remark only to be called after start of KRNL @remark no chk of if it is a valid task */ int k_stk_chk(struct k_t *t); /** Returns amount of unused stak @param[in] t Ptr to taskdescriptor. If NULL it is yourself @return: amount of unused stak(in bytes) @remark: a watermark philosophy is used **/ int k_unused_stak(struct k_t *t); /** * round robbin * reinsert running task in activeQ. * so running will be inserted after other tasks with same or higher priority **/ void k_round_robbin(void); /** * release * switch running to task in front of Active Q * calls ki_task_shift (); enveloped by a DI and EI * no round robbin **/ void k_release(void); /** */ void k_set_coop_multitask(unsigned char onn); /** * returns amount of free memory in your system * (free size of heap) se docu in krnl.c */ int freeRam(void); #ifdef KRNLBUG /** * Breakout function called from scheduler * You can use to examine who is runnning and who is next * NB NB should be fast code and remember interrupt is and must be disables * pRun->nr Nr of running. * dummy has nr == 0 * then task is numbered 1.. in acc with order of creation of tasks * k_breakout is defined as weak function so you can write your own k_breakout * The builtin k_breakout has no code and just a placeholder to be subst * with your code. * setting led8-12 on uno as task number * PORTB = (1 << pRun->nr) | led13 **/ void __attribute__((weak)) k_breakout(void); #endif #ifdef __cplusplus } #endif #endif // #ifndef KRNL /******************************************************* * * * * * | |/ /___| _ \| \ | | ___| | * * | ' // _ \ |_) | \| |/ _ \ | * * | . \ __/ _ <| |\ | __/ |___ * * |_|\_\___|_| \_\_| \_|\___|_____| * * * * * * you are watching krnl.c * * * * March 2015,2016,..,2018 * * Author: jdn * * final sep 2022 * * * ******************************************************* you are watching krnl.c March 2015,2016,..,2018 Author: jdn final sep 2022 2022 .. this version adapted for Arduino (C) 2012,2013,2014 2017,2018,2019,2021,2022 IF YOU ARE LUCKY LOOK HERE https://github.com/jdn-aau/krnl Jens Dalsgaard Nielsen <jdn@es.aau.dk> http://es.aau.dk/staff/jdn Section of Automation & Control Aalborg University, Denmark "THE BEER-WARE LICENSE" (frit efter PHK) <jdn@es.aau.dk> wrote this file. As long as you retain this notice you can do whatever you want with this stuff. If we meet some day, and you think this stuff is worth it ... you can buy me a beer in return :-) or if you are real happy then ... single malt will be well received :-) Use it at your own risk - no warranty nice info... http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus at 20090611 - k_eat_time now eats time in quants of krnl tick speed but not one quant boundary - added k_malloc and k_free k_free dont free memory bq we do not want fragmentation so DONT USE k_free - k_malloc and k_free are weak functions so you can just add your own versions - watchdog is enabled (2sec and reset in krnl timer) in weak function k_enable_wdt ************************************************************************/ #include "krnl.h" #ifdef WDT_TIMER #include <avr/wdt.h> #endif #include <avr/interrupt.h> #include <util/delay.h> #include <stdlib.h> // CPU frequency - for adjusting delays #if (F_CPU == 8000000) #pragma message("krnl detected 8 MHz") #endif #if (KRNL_VRS != 20221027) #error "KRNL VERSION NOT UPDATED in krnl.c " #endif /* #ifdef __cplusplus extern "C" { #endif */ /* which timer to use for krnl heartbeat timer 0 ( 8 bit) is normally used by millis - avoid ! or modify TIMER0 in wiring.c timer 1 (16 bit) DEFAULT timer 2 ( 8 bit) timer 3 (16 bit) 1280/1284P/2560 only (MEGA) timer 4 (16 bit) 1280/2560 only (MEGA) timer 5 (16 bit) 1280/2560 only (MEGA) */ #if (KRNLTMR == 0) JDN SIGER NOGO IN SIMPLIFY VRS // normally not goood bq of arduino sys timer is on timer 0so you wil get a // compile error 8 bit timer !!! //#define KRNLTMRVECTOR TIMER0_OVF_vect /* we use setting from original timer0 #define TCNTx TCNT0 #define TCCRxA TCCR0A #define TCCRxB TCCR0B #define TCNTx TCNT0 #define OCRxA OCR0A #define TIMSKx TIMSK0 #define TOIEx TOIE0 #define PRESCALE 0x07 #define COUNTMAX 255 #define DIVV 15.625 #define DIVV8 7.812 */ #elif (KRNLTMR == 1) #define KRNLTMRVECTOR TIMER1_OVF_vect #define TCNTx TCNT1 #define TCCRxA TCCR1A #define TCCRxB TCCR1B #define TCNTx TCNT1 #define OCRxA OCR1A #define TIMSKx TIMSK1 #define TOIEx TOIE1 #define PRESCALE 0x03 #define COUNTMAX 0xffff #define DIVV 250 #define DIVV8(DIVV / 2) #elif (KRNLTMR == 2) // 8 bit timer !!! // standard for krnl CHECK which pwm, tone etc is on this timer #pragma warn "krnl timer on timer 2 - check PWM " #define KRNLTMRVECTOR TIMER2_OVF_vect #define TCNTx TCNT2 #define TCCRxA TCCR2A #define TCCRxB TCCR2B #define TCNTx TCNT2 #define OCRxA OCR2A #define TIMSKx TIMSK2 #define TOIEx TOIE2 #define PRESCALE 0x05 #define COUNTMAX 255 #define DIVV 125 #define DIVV8 64 #elif (KRNLTMR == 3) #define KRNLTMRVECTOR TIMER3_OVF_vect #define TCNTx TCNT3 #define TCCRxA TCCR3A #define TCCRxB TCCR3B #define TCNTx TCNT3 #define OCRxA OCR3A #define TIMSKx TIMSK3 #define TOIEx TOIE3 #define PRESCALE 0x03 #define COUNTMAX 0xffff #define DIVV 250 #define DIVV8(DIVV / 2) #elif (KRNLTMR == 4) #define KRNLTMRVECTOR TIMER4_OVF_vect #define TCNTx TCNT4 #define TCCRxA TCCR4A #define TCCRxB TCCR4B #define TCNTx TCNT4 #define OCRxA OCR4A #define TIMSKx TIMSK4 #define TOIEx TOIE4 #define PRESCALE 0x03 #define COUNTMAX 0xffff #define DIVV 250 #define DIVV8(DIVV / 2) #elif (KRNLTMR == 5) #define KRNLTMRVECTOR TIMER5_OVF_vect #define TCNTx TCNT5 #define TCCRxA TCCR5A #define TCCRxB TCCR5B #define TCNTx TCNT5 #define OCRxA OCR5A #define TIMSKx TIMSK5 #define TOIEx TOIE5 #define PRESCALE 0x03 #define COUNTMAX 0xffff #define DIVV 250 #define DIVV8(DIVV / 2) #else #pragma err "KRNL: no valid tmr selected" #endif //---------------------------------------------------------------------------- struct k_t *task_pool, // array of descriptors for tasks *sem_pool, // .. for semaphores AQ, // Q head for active Q *pmain_el, // procesdecriptor for main *pAQ, // head of activeQ (AQ) // *pDmy, // ref to dummy task *pRun, // who is running ? *pSleepSem; // one semaphor for all to sleep at struct k_msg_t *send_pool; // ptr to array for msg sem pool int k_task, k_sem, k_msg; // how many did you request in k_init of descriptors ? char nr_task = 0, nr_sem = 0, nr_send = 0; // counters for created KeRNeL items volatile char k_running = 0, k_err_cnt = 0; #ifdef WDT_TIMER volatile char k_wdt_enabled = 1; #endif volatile unsigned int tcntValue; // counters for timer system unsigned long k_millis_counter = 0; unsigned int k_tick_size; unsigned char k_coopFlag=0; int tmr_indx; // for travelling Qs in tmr isr //---QOPS--- double chained lists with qhead as a element /* ------------------------------------------------ | | \/ | QHEAD (next)-->first-elm (next)-->next-elm(next) --| ^ ^ ------------(pred) -----------(pred) char s1[150],s2[150],s3[150],s4[150]; v *oid setup() { Serial.begin(115200); // for output from task 1 delay(2000); Serial.print("RW START"); k_init(4, 3, 0); k_crt_task(wr, 12, s1,150); k_crt_task(rd, 11, s2,150); k_crt_task(rd, 11, s3,150); k_crt_task(rd, 11, s4,150); // rdWrLockInit(&rdWrLock1); k_start(); // start kernel with tick speed 1 milli seconds Serial.println("if coming hre then init went wrong"); } */ void enQ(struct k_t *Q, struct k_t *el) { el->next = Q; el->pred = Q->pred; Q->pred->next = el; Q->pred = el; } struct k_t *deQ(struct k_t *el) { el->pred->next = el->next; el->next->pred = el->pred; return (el); } void prio_enQ(struct k_t *Q, struct k_t *el) { char prio = el->prio; Q = Q->next; // bq first elm is Q head itself while (Q->prio <= prio) // find place before next with lower prio { Q = Q->next; } el->next = Q; // insert before element referred by Q el->pred = Q->pred; Q->pred->next = el; Q->pred = el; } /***** eat time ***/ void k_eat_msec(unsigned int eatTime) { while (10 < eatTime) { delayMicroseconds(10000); eatTime -= 10; } delayMicroseconds(eatTime * 1000); } //---HW timer IRS--timer section------------------------ /* The KRNL Timer is driven by timer Install the Interrupt Service Routine (ISR) for Timer2 overflow. This is normally done by writing the address of the ISR in the interrupt vector table but conveniently done by using ISR() Timer2 reload value, globally available */ struct k_t *pE; /* from wiring.c // the prescaler is set so that timer0 ticks every 64 clock cycles, and the // the overflow handler is called every 256 ticks. #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) // the whole number of milliseconds per timer0 overflow #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) // the fractional number of milliseconds per timer0 overflow. we shift right // by three to fit these numbers into a byte. (for the clock speeds we care // about - 8 and 16 MHz - this doesn't lose precision.) #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) #define FRACT_MAX (1000 >> 3) which is */ ISR(KRNLTMRVECTOR, ISR_NAKED) // naked so we have to supply with prolog and // epilog (push pop stack of regs) { PUSHREGS(); // no local vars ! I think TCNTx = tcntValue; // Reload the timer #ifdef WDT_TIMER if (k_wdt_enabled) wdt_reset(); #endif if (!k_running) { goto exitt; } k_millis_counter += k_tick_size; // my own millis counter // the following may look crazy: to go through all semaphores and tasks // but you may have 3-4 tasks and 3-6 semaphores in your code // so - seems to be efficient :-) // so - it's a good idea not to init krnl with more items // (tasks/Sem/msg descriptors than needed) pE = sem_pool; // Semaphore timer - check timers on semaphores for (tmr_indx = 0; tmr_indx < nr_sem; tmr_indx++) { if (0 < pE->cnt2) // timer on semaphore ? { pE->cnt2--; // yep decrement it if (pE->cnt2 <= 0) // timeout ? { pE->cnt2 = pE->cnt3; // preset again - if cnt3 == 0 and >= 0 the rep timer ki_signal(pE); // issue a signal to the semaphore } } pE++; } pE = task_pool; // Chk timers on tasks - they may be one shoot waiting for (tmr_indx = 0; tmr_indx < nr_task; tmr_indx++) { if (0 < pE->cnt2) // timer active on task ? { pE->cnt2--; // yep so let us do one down count if (pE->cnt2 <= 0) // timeout ? ( == 0 ) { ((struct k_t *)(pE->cnt3)) ->cnt1++; // leaving sem so adjust semcount on sem prio_enQ(pAQ, deQ(pE)); // and rip task of semQ and insert in activeQ pE->cnt2 = -1; // indicate timeout in this semQ for the task that is restartet } } pE++; } if (!k_coopFlag) { prio_enQ(pAQ, deQ(pRun)); // round robbin K_F_CHG_STAK(); } exitt: POPREGS(); RETI(); } /**HERE**/ //---------------------------------------------------------------------------- // inspired from ... // http://arduinomega.blogspot.dk/2011/05/timer2-and-overflow-interrupt-lets-get.html // Inspiration from // http://popdevelop.com/2010/04/mastering-timer-interrupts-on-the-arduino/ // Inspiration from "Multitasking on an AVR" by Richard Barry, March 2004 // and http://www.control.aau.dk/~jdn/kernels/krnl/ //---------------------------------------------------------------------------- // avrfreaks.net // and my old kernel from last century // and a lot other stuff // basic concept from my own very old kernels dated back bef millenium void __attribute__((naked, noinline)) ki_task_shift(void) { PUSHREGS(); // push task regs on stak so we are rdy to task shift K_CHG_STAK(); // find taskstak for task in front of activeQ POPREGS(); // restore regs RETI(); // and do a reti NB this also enables interrupt !!! } #ifdef BACKSTOPPER void jumper() { while (1) (*(pRun->pt))(); // call task code again and again //k_set_prio(ZOMBI_PRIO); // priority lower than dummy so you just stops //while (1) // ; // just in case } #endif struct k_t *k_crt_task(void (*pTask)(void), char prio, char *pStk, int stkSize) { struct k_t *pT; int i; char *s; if ((k_running) || ((prio <= 0) || (DMY_PRIO < prio)) || (k_task <= nr_task)) { goto badexit; } if (pStk == NULL) { // you didnt give me a stack goto badexit; } pT = task_pool + nr_task; // lets take a task descriptor pT->nr = nr_task; nr_task++; pT->cnt2 = 0; // no time out running on you for the time being pT->cnt3 = 0; // no time out semaphore pT->cnt1 = (int)(pStk); // ref to my stack // paint stack with hash code to be used by k_unused_stak() for (i = 0; i < stkSize; i++) { pStk[i] = STAK_HASH; } s = pStk + stkSize - 1; // now we point on top of stak *(s--) = 0x00; // 1 byte safety distance :-) // an interrupt do only push PC on stack by HW - can be 2 or 3 bytes // depending of 368/.../1280/2560 #ifdef BACKSTOPPER pT->pt = pTask; *(s--) = lo8(jumper); // so top now holds address of function *(s--) = hi8(jumper); // which is code body for task #else *(s--) = lo8(pTask); // so top now holds address of function *(s--) = hi8(pTask); // which is code body for task #endif // NB NB 2560 use 3 byte for call/ret addresses the rest only 2 #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) (s--) = EIND; // best guess : 3 byte addresses !!! or just 0 #endif // r1 is the socalled zero value register // see https://gcc.gnu.org/wiki/avr-gcc // can tmp be non zero (multiplication etc) *(s--) = 0x00; // r1 *(s--) = 0x00; // r0 *(s--) = 0x00; // sreg // 1280 and 2560 need to save rampz reg just in case #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) || \ defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega2561__) (s--) = RAMPZ; // best guess 0x3b // obsolete JDN *(s--) = EIND; // best guess #endif #if defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1280__) || \ defined(__AVR_ATmega2561__) (s--) = EIND; // best guess 0x3c #endif for (i = 0; i < 30; i++) // r2-r31 = 30 regs { *(s--) = 0x00; } pT->sp_lo = lo8(s); // now we just need to save stakptr pT->sp_hi = hi8(s); // in thread descriptor // HW DEPENDENT PART - ENDE pT->prio = prio; // maxv for holding org prio for inheritance pT->maxv = (int)prio; prio_enQ(pAQ, pT); // and put task in active Q return (pT); badexit: k_err_cnt++; return (NULL); } int freeRam(void) { extern int __heap_start, *__brkval; int x, v; // NASTY x = ((int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval)); // to remove warning return x; // hw specific :-/ // return ((int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval)); /* from http://www.noah.org/wiki/Arduino_notes#debug_memory_problems_.28out_of_RAM.29 int freeRam () { // __brkval is the address of the top of the heap if memory has been allocated. // If __brkval is zero then it means malloc has not used any memory yet, so // we look at the address of __heap_start. extern int __heap_start extern int *__brkval; // address of the top of heap int stack_top; return (int)&stack_top - ((int)__brkval == 0 ? (int)&__heap_start : (int)__brkval); } */ } int k_sleep(int time) { int r; if (time <= 0) return -1; r = k_wait(pSleepSem, time); if (r == -1) // timeout ? yes :-) return 0; } int k_unused_stak(struct k_t *t) { int i; char *pstk; if (t) // another task or yourself - NO CHK of validity !!!!! { pstk = (char *)(t->cnt1); } else { pstk = (char *)(pRun->cnt1); } DI(); // look for stack paint i = 0; while (*pstk == STAK_HASH) { pstk++; i++; } EI(); return (i); } int k_set_prio(char prio) { int i; if (!k_running) { return (-2); } DI(); if ((prio <= 0) || (DMY_PRIO <= prio)) // not legal value my friend { EI(); return (-1); } i = pRun->prio; pRun->prio = prio; prio_enQ(pAQ, deQ(pRun)); ki_task_shift(); EI(); return (i); } int k_mut_ceil_set(struct k_t *sem, char prio) { // NB NB assume semaphore is created prior to this call // NO CHECK - no mercy !!! // work only BEFORE k_start if (k_running) { return (-2); // bad bad } if ((prio <= 0) || (DMY_PRIO <= prio)) { return (-1); // bad bad } sem->ceiling_prio = prio; return 0; // OK } struct k_t *k_crt_sem(int init_val, int maxvalue) { struct k_t *sem; if (k_running) { return (NULL); } if ((maxvalue < init_val) || (SEM_MAX_VALUE < maxvalue) || (init_val < 0) || (maxvalue < 0) ) { goto badexit; } if (k_sem <= nr_sem) // no vacant in buf { goto badexit; } sem = sem_pool + nr_sem; // allocate it sem->nr = nr_sem; nr_sem++; sem->cnt2 = 0; // no timer running sem->next = sem->pred = sem; // point at myself == no one in Q sem->prio = QHD_PRIO; sem->cnt1 = init_val; sem->maxv = maxvalue; sem->clip = 0; sem->ceiling_prio = -1; // to indicate it is not active sem->saved_prio = -1; return (sem); badexit: k_err_cnt++; // one more error so krnl will not start return (NULL); } int k_set_sem_timer(struct k_t *sem, int val) { // there is no k_stop_sem_timer fct just call with val== 0 for stopping timer // fct if (val <= 0) { return -1; } DI(); if (0 < sem->cnt1) { sem->cnt1 = 0; // reset } sem->cnt2 = sem->cnt3 = val; // if 0 then timer is not running - EI(); return (0); } int ki_signal(struct k_t *sem) { DI(); // just in case if (sem->cnt1 < sem->maxv) { sem->cnt1++; // Salute to Dijkstra #ifdef KRNLBUG k_sem_signal(sem->nr, sem->cnt1); // call to breakout functino #endif if (sem->cnt1 <= 0) { sem->next->cnt2 = 0; // return code == ok in waiting tasks pocket(cnt2) prio_enQ(pAQ, deQ(sem->next)); return (0); // a task was waiting and is now in AQ } else { return (1); // just delivered a signal - no task was waiting } } // CLIP :-( else { if (sem->clip < MAX_SEM_VAL + 1) { sem->clip++; } // here we are on bad clip failure no signal takes place // signal is lost !!! #ifdef KRNLBUG k_sem_clip(sem->nr, sem->clip); #endif return (-1); } } int k_signal(struct k_t *sem) { int res; DI(); res = ki_signal(sem); // 1: ok no task to AQ, 0: ok task to AQ if (res == 0) { ki_task_shift(); // bq maybe started task has higher prio than me } EI(); return (res); } /* normally ki_wait should not be used by user */ int ki_wait(struct k_t *sem, int timeout) { DI(); if (0 < sem->cnt1) { sem->cnt1--; // Salute to Dijkstra return (1); // ok: 1 bq we are not suspended } if (timeout < 0) // no luck, dont want to wait so bye bye { return (-1); // will not wait so bad luck } // from here we want to wait pRun->cnt2 = timeout; // 0 == wait forever if (timeout) { // so we can be removed if timeout occurs pRun->cnt3 = (int)sem; // nasty keep ref to semaphore in task stomach } sem->cnt1--; // Salute to Diocjkstra enQ(sem, deQ(pRun)); ki_task_shift(); // back again - have semaphore received signal or timeout ? pRun->cnt3 = 0; // reset ref to timer semaphore return ((char)(pRun->cnt2)); // 0: ok , -1: timeout } int k_wait(struct k_t *sem, int timeout) { int retval; DI(); retval = ki_wait(sem, timeout); EI(); return retval; // 0: ok, -1: timeout } int k_clear_sem(struct k_t *sem) { int retval; DI(); retval = sem->cnt1; if (0 < retval) { sem->clip += retval; sem->cnt1 = 0; } EI(); return retval; // 0: ok, -1: timeout } int k_wait2(struct k_t *sem, int timeout, int *nrClip) { int retval; DI(); retval = ki_wait(sem, timeout); if (nrClip) { *nrClip = sem->clip; sem->clip = 0; } EI(); return retval; // 0: ok, -1: timeout } struct k_t *k_crt_mut(int ceiling_prio, int init_val, int maxvalue) { struct k_t *mut; if (k_running) { return (NULL); } if (ceiling_prio < 0) { k_err_cnt++; return NULL; } mut = k_crt_sem(init_val, maxvalue); if (mut == NULL) { k_err_cnt++; return NULL; } mut->ceiling_prio = ceiling_prio; return mut; } int k_mut_ceil(struct k_t *sem, int timeout, void (*fct)(void)) { int r; r = k_mut_ceil_enter(sem, timeout); if (r < 0) { return r; // bad bad } (*fct)(); // call mutex function k_mut_ceil_leave(sem); return r; } int k_mut_ceil_enter(struct k_t *sem, int timeout) { int retval; DI(); if (sem->ceiling_prio < 0) { EI(); return CEILINGFAILNOTCEIL; } if (pRun->prio < sem->ceiling_prio) { // I have higher priority than ceiling :-( EI(); return CEILINGFAILPRIO; } // now we play imm ceiling protocol sem->saved_prio = pRun->prio; // do im ceiling pRun->prio = sem->ceiling_prio; // dont need to reinsert in AQ bq ceil prio is higher // or equal to mine and Im already in front of AQ prio_enQ(pAQ, deQ(pRun)); // resinsert me in AQ acc to nwe(old) priority retval = ki_wait(sem, timeout); // coming back interrupt is still disabled ! // chk if we did get semaphore if (retval < 0) { // NOPE we did not pRun->prio = sem->saved_prio; // reset to my old priority prio_enQ(pAQ, deQ(pRun)); // reinsert me in AQ acc to nwe(old) priority ki_task_shift(); // bq maybe started task has higher prio than me } EI(); return retval; // 0(has waited),1(straight through) : ok, -1: timeout } int k_mut_ceil_leave(struct k_t *sem) { int res; DI(); res = ki_signal(sem); // 1: ok no task to AQ, 0: ok task to AQ // coming back interrupt is still disabled ! pRun->prio = sem->saved_prio; // reset to my old priority prio_enQ(pAQ, deQ(pRun)); // resinsert me in AQ acc to nwe(old) priority ki_task_shift(); // bq maybe started task has higher prio than me EI(); return (res); } int k_sem_signals_lost(struct k_t *sem) { int x; DI(); x = sem->clip; sem->clip = 0; EI(); return x; } int ki_semval(struct k_t *sem) { DI(); // dont remove this - bq k_semval depends on it return (sem->cnt1); } int k_semval(struct k_t *sem) { int v; v = ki_semval(sem); EI(); return v; } int ki_msg_count(struct k_msg_t *m) { DI(); // dont remove this - bq k_semval depends on it return m->cnt; } int k_msg_count(struct k_msg_t *m) { int v; // not needed to DI - its in ki_msg_count ... DI (); v = ki_msg_count(m); EI(); return v; } struct k_msg_t *k_crt_send_Q(int nr_el, int el_size, void *pBuf) { struct k_msg_t *pMsg; if (k_running) { return (NULL); } if (k_msg <= nr_send) { goto errexit; } if (k_sem <= nr_sem) { goto errexit; } pMsg = send_pool + nr_send; pMsg->nr = nr_send; // I am element nr nr_send in msgQ pool nr_send++; pMsg->sem = k_crt_sem(0, nr_el); // we are using a sem for sync part snd <-> rcv if (pMsg->sem == NULL) { goto errexit; } pMsg->pBuf = (char *)pBuf; pMsg->r = pMsg->w = -1; pMsg->el_size = el_size; pMsg->nr_el = nr_el; pMsg->lost_msg = 0; pMsg->cnt = 0; // count nr elm in Q return (pMsg); errexit: k_err_cnt++; return (NULL); } int ki_clear_msg_Q(struct k_msg_t *pB) { int ret; if (k_running) { return -2; } ret = pB->cnt; if (0 < ret) { // messages pending s0 pB->lost_msg = 0; pB->cnt = 0; // reset pB->r = pB->w = -1; // clear sem - can do it bq no one is waiting bq 0 < ret == pending // messages pB->sem->cnt1 = 0; // Serious NASTY } return ret; } int k_clear_msg_Q(struct k_msg_t *pB) { int r; DI(); r = ki_clear_msg_Q(pB); EI(); return r; } char ki_send(struct k_msg_t *pB, void *el) { int i; char *pSrc, *pDst; if (pB->nr_el <= pB->cnt) { // nope - no room for a putting new msg in Q ? if (pB->lost_msg < SEM_MAX_VALUE) { pB->lost_msg++; } #ifdef KRNLBUG k_send_Q_clip(pB->nr, pB->lost_msg); #endif return (-1); // nope } else { pB->cnt++; pSrc = (char *)el; pB->w++; if (pB->nr_el <= pB->w) // simple wrap around { pB->w = 0; } pDst = pB->pBuf + (pB->w * pB->el_size); // calculate where we shall put msg in ringbuf for (i = 0; i < pB->el_size; i++) { // copy to Q *(pDst++) = *(pSrc++); } return (ki_signal(pB->sem)); // indicate a new msg is in Q } } char k_send(struct k_msg_t *pB, void *el) { char res; DI(); res = ki_send(pB, el); if (res == 0) // if new task in AQ == someone was waiting for msg { // if 1 then nobody was waiting so no neeed for task shift ki_task_shift(); } EI(); return (res); } char ki_receive(struct k_msg_t *pB, void *el, int *lost_msg) { int i; char r, *pSrc, *pDst; // can be called from ISR bq no blocking DI(); // just to be sure if (0 <= (r = ki_wait(pB->sem, -1))) { pDst = (char *)el; pB->r++; pB->cnt--; // got one if (pB->nr_el <= pB->r) { pB->r = 0; } pSrc = pB->pBuf + pB->r * pB->el_size; for (i = 0; i < pB->el_size; i++) { *(pDst++) = *(pSrc++); } if (lost_msg) { *lost_msg = pB->lost_msg; pB->lost_msg = 0; } return (r); // yes } return (-1); // nothing for you my friend } char k_receive(struct k_msg_t *pB, void *el, int timeout, int *lost_msg) { int i; char r, *pSrc, *pDst; DI(); if (0 <= (r = ki_wait(pB->sem, timeout))) { // yes we did get a msg :-) // ki_wait bq then intr is not enabled when coming back pDst = (char *)el; pB->r++; pB->cnt--; // got one if (pB->nr_el <= pB->r) { pB->r = 0; } pSrc = pB->pBuf + pB->r * pB->el_size; for (i = 0; i < pB->el_size; i++) { *(pDst++) = *(pSrc++); } if (lost_msg) { *lost_msg = pB->lost_msg; pB->lost_msg = 0; } EI(); return (r); // 1 if no suspension bq msg was already present, 0: ok if you // have waited on msg } EI(); return (-1); // nothing for you my friend } #ifdef READERWRITER // https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem void k_rwInit(struct k_rwlock_t *lock) { lock->nrReaders = 0; lock->rdwrSem = k_crt_sem(1, 2); lock->rdSem = k_crt_sem(1, 2); lock->fifoSem = k_crt_sem(1, 2); } int k_rwRdEnter(struct k_rwlock_t *lock, int timeout) { // timeout tbi later - if... k_wait(lock->fifoSem, 0); k_wait(lock->rdSem, 0); lock->nrReaders++; if (lock->nrReaders == 1) k_wait(lock->rdwrSem, 0); k_signal(lock->fifoSem); k_signal(lock->rdSem); } int k_rwWrEnter(struct k_rwlock_t *lock, int timeout) { k_wait(lock->fifoSem, 0); k_wait(lock->rdwrSem, 0); k_signal(lock->fifoSem); } int k_rwRdLeave(struct k_rwlock_t *lock) { k_wait(lock->rdSem, 0); lock->nrReaders--; if (lock->nrReaders == 0) { k_signal(lock->rdwrSem); } k_signal(lock->rdSem); } int k_rwWrLeave(struct k_rwlock_t *lock) { k_signal(lock->rdwrSem); } #endif void k_round_robbin(void) { // reinsert running task in activeQ if round robbin is selected DI(); prio_enQ(pAQ, deQ(pRun)); ki_task_shift(); EI(); } void k_release(void) { // let next run DI(); ki_task_shift(); EI(); } /* NASTY void from vrs 2001 it is main itself can be changed back */ /* void dummy_task (void) { while (1) { k_round_robbin (); } } */ // char dmy_stk[DMY_STK_SZ]; // dmy duty is nwo maintained by org main int k_init(int nrTask, int nrSem, int nrMsg) { if (k_running) { return (-666); } k_task = nrTask + 1; // +1 due to dummy k_sem = nrSem + nrMsg + 1; // due to that every msgQ has a builtin semaphore k_msg = nrMsg + 1; // to align so first user msgQ has index 1 nr_send++; // to align so we waste one but ... better equal access task_pool = (struct k_t *)malloc(k_task * sizeof(struct k_t)); sem_pool = (struct k_t *)malloc(k_sem * sizeof(struct k_t)); send_pool = (struct k_msg_t *)malloc(k_msg * sizeof(struct k_msg_t)); // we dont accept any errors if ((task_pool == NULL) || (sem_pool == NULL) || (send_pool == NULL)) { k_err_cnt++; goto leave; } // init AQ as empty double chained list pAQ = &AQ; pAQ->next = pAQ->pred = pAQ; pAQ->prio = QHD_PRIO; // crt dummy // pDmy = k_crt_task (dummy_task, DMY_PRIO, DMY_STK_SZ); pmain_el = task_pool; pmain_el->nr = 0; pmain_el->cnt2 = pmain_el->cnt3 = 0; nr_task++; pmain_el->prio = DMY_PRIO; // main is dummy prio_enQ(pAQ, pmain_el); pSleepSem = k_crt_sem(0, 10); leave: return k_err_cnt; } int k_start(void) { /* TCCRxB 48,88,168,328, 1280,2560 timer 0 and 2 has same prescaler config: 8 bit: 0 0 0 No clock source (Timer/Counter stopped). 0 0 1 clk T2S /(No prescaling) 0 1 0 clk T2S /8 (From prescaler) 2000000 intr/sec at 1 downcount 0 1 1 clk T2S /32 (From prescaler) 500000 intr/sec ... 1 0 0 clk T2S /64 (From prescaler) 250000 1 0 1 clk T2S /128 (From prescaler) 125000 1 1 0 clk T 2 S /256 (From prescaler) 62500 1 1 1 clk T 2 S /1024 (From prescaler) 15625 eq 15.625 count down for 1 millisec so 255 counts ~= 80.32 milli sec timer timer 1(328+megas), 3,4,5(megas only) 1280, 2560,2561 has same prescaler config : FOR 16 bits ! prescaler in cs2 cs1 cs0 0 0 0 none 0 0 1 /1 == none 0 1 0 /8 2000000 intr/sec 0 1 1 /64 250000 intr/sec 1 0 0 /256 62500 intr/sec 1 0 1 /1024 15625 intr/sec 16MHz Arduino -> 16000000/1024 = 15625 intr/second at one count 16MHz Arduino -> 16000000/256 = 62500 ticks/second -------------------------/64 = 250000 ticks/second ! NB 16 bit counter so values >= 65535 is not working ************************************************************************************* */ int tm = 1; // always 1 msec if (tm != 1) { return -999; } // will not start if errors during initialization if (k_err_cnt) { return -k_err_cnt; } // boundary check if (tm <= 0) { return -555; } k_tick_size = tm; DI(); // silencio // outdated ? JDN NASTY #if defined(__AVR_ATmega32U4__) // 32u4 have no intern/extern clock source register #else // should be default ASSR &= ~(1 << AS2); // Select clock source: internal // I/O clock 32u4 does not have this facility #endif #if (KRNLTMR != 0) TCCRxA = 0; TCCRxB = PRESCALE; // atm328s 2560,... if (F_CPU == 16000000L) { tcntValue = COUNTMAX - DIVV; } else { tcntValue = COUNTMAX - DIVV8; // 8 Mhz wwe assume } TCNTx = tcntValue; // let us start the show TIMSKx |= (1 << TOIEx); // enable interrupt #endif // if timer 0 we will always run 1 msec !!!! DI(); pRun = pmain_el; // just for ki_task_shift k_running = 1; ki_task_shift(); // bye bye from here EI(); // this while loop bq main are dummy while (1) { } return 0; } int k_stop() { /* main is dummy task so it gives no meaning to stop krnl this way The best and dirty thing is her DI(); while (1); */ // DANGEROUS - handle with care - no isr timer control etc etc // I WILL NEVER USE IT DI(); // silencio if (!k_running) { EI(); return -1; } while (1) ; // we stuck here with intr disabled !!! } void k_set_coop_multitask(unsigned char onn) { k_coopFlag = onn; } unsigned long ki_millis(void) { unsigned long l; DI(); // just to be sure l = k_millis_counter; return l; } unsigned long k_millis(void) { unsigned long l; DI(); l = k_millis_counter; EI(); return l; } int k_tmrInfo(void) { return (KRNLTMR); } #ifdef KRNLBUG // defined as weak so compiler will take yours instead of mine void __attribute__((weak)) k_breakout(void) {} void __attribute__((weak)) k_sem_clip(unsigned char nr, int nrClip) {} void __attribute__((weak)) k_sem_signal(unsigned char nr, int semVal) {} void __attribute__((weak)) k_sem_wait(unsigned char nr, int semVal) {} void __attribute__((weak)) k_send_Q_clip(unsigned char nr, int nrClip) {} #endif #ifdef DYNMEMORY void *__attribute__((weak)) k_malloc(int k) { void *m; DI(); m = malloc(k); EI(); return m; } void __attribute__((weak)) k_free(void *m) { // we dont free memory } #endif void __attribute__((weak)) k_wdt_enable(int i) { DI(); wdt_enable(i); k_wdt_enabled = 1; EI(); } void __attribute__((weak)) k_wdt_disable(void) { DI(); k_wdt_enabled = 0; wdt_disable(); EI(); } /* EOF - JDN */ /* #ifdef __cplusplus } #endif */ And some examples And the rest krnl howto for ArduinoThe proper place to put them in your Arduino environment:
Please note the cpp naming and that directory in libraries has same name as the cpp file For using it in your code start with sketch>Import Libray>Contributed and select krnl Then it will add a standard include <krnl.h> in your sketch and you are rdy to go
Start Arduino SW select Sketch -> Import Library -> krnl It will add If you want to have the examples I have provided (not all are bug free I think) then you have to …
You can now find them in File->Sketchbook->krnl->“examples…” For getting a copy for yourself: might not be uptodate git clone git://github.com/jdn-aau/krnl /Jens PerformanceA task shift takes approx 21 usec. See perf.html for the test program HOWTOSee config KeRNeL discover type of your board so no need to change setup by hand in krnl.h See config for a short intro to configuration and RAM usage. happy hacking Jens
Jens Dalsgaard Nielsen . CC BY-SA 4.0 ,2023-03-27 15:06:01 CEST
|