KRNL - Debugging
For breakout functions for intercept taskshift etc see breakout.html
It's possible to assert signals on pins
Krnl has a number of breakout functions you can implement
Here is one version - which is just in the bottom of your sketchfile
Some remarks
Due to pcb layout and internal cpu structure the IO pins on the Arduinos (uneo,mega,..) does not refer to same internal registers aka ports.
See pins and ports here
Below is shown Arduino digital pins which can be used for debugging. But there are ofcourse others available.
The ports below maps ordered IO pins (like 8,9,… on UNO) to one internal register which ease use for debugging.
YOu can find others - its up to you
(click for code)
UNO
pin port
8 PB0
9 PB1
10 PB2
11 PB3
12 PB4
13 PB5 LED13
PB6 and 7 etc not to be used !
MEGA
pin port
22 PA0
34 PA1
24 PA2
25 PA3
26 PA4
27 PA5
28 PA6
29 PA7
13 PB7 LED13
MICRO
8 PB4
9 PB5
10 PB6
11 PB7
13 PC7 LED13
NANO
D8 PD0
D9 PD1
D2 PD2
D3 PD3
D4 PD4
D5 PD5
D6 PD6
D7 PD7
13 PB5 LED13
PRO MINI
2 PD2
3 PD3
4 PD4
5 PD5
6 PD6
13 PB5 LED13
Krnl breakout function
k_breakout is considered the most important debug facility in krnl.
k_breakout is called for every taskshift in krnl.
It is called after pRun is set to point to the task to become running
You can identify the running by pRun->nr
0: dummy task - cpu time eater when nobody else want to rn
1,2,3 your task numbered in same order as their respective k_crt_task calls
So we can identify the running task nr (pRun->nr)
On an UNO
PORTB bits is connected to digital pins 8,9,10,11,12,13
We will use PORTB in the followin way
PORTB bit 0 high when nr 0 (aka dummy is running)
PORTB bit 1 high when first created task is running
PORTB bit 2 high when second created task is running
etc
You can only use PORTB d0..d5, D6b and d6 is used for the 16MHz xtal
You may connect a logic analyser (like analog discovery) to D8-D13
// UNO UNO UNO
// init the digital pins (place it in setup)
for (int i=0; i < 14 ; i++) {
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
// and place the following outside any function
extern "C" {
void k_breakout() // called every task shift from dispatcher
{
PORTB = (1 << pRun->nr);
}
}
On MEGA
Basicly the same just on PORTA
PORTA bit 0: 22
PORTA bit 2: 23
etc
// MEGA MEGA
// init the digital pins (place it in setup)
for (int i=2; i < 30 ; i++) {
pinMode(i,OUTPUT);
digitalWrite(i,LOW);
}
// and place the following outside any function
extern "C" {
void k_breakout() // called every task shift from dispatcher
{
PORTA = (1 << pRun->nr);
}
}
No analyser - use LED13
If you do not have a analyser or a handfull of LEDs youcan get some info
by turning ON led13 when dummy is running.
If running is running from time to time there may be enough cpu time.
But beware we can not say anything about realtime requirements.
// UNO UNO UNO
// init the digital pins (place it in setup)
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
// and place the following outside any function
extern "C" {
void k_breakout() // called every task shift from dispatcher
{
if (pRun->nr == 0)
PORTB |= 0x20; // ON - LED13 is bit 5 0010000 aka 0x20
else
PORTB &= B11011111; // OFF 0xcf
}
}
// MEGA
// init the digital pins (place it in setup)
// LED is PORTB bit 7
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
// and place the following outside any function
extern "C" {
void k_breakout() // called every task shift from dispatcher
{
if (pRun->nr == 0)
PORTB |= 0x80; // ON - LED13 is bit7 10000000 aka 0x20
else
PORTB &= B01111111; // OFF 0xcf
}
}
See here for at number of small programs with traces from analog discovery
/* or just LED13 if dummy ...
YOu can test for board by #ifdef ARDUINO_AVR_UNO etc
see list below
AVR_ADK
AVR_BT
AVR_DUEMILANOVE
AVR_ESPLORA
AVR_ETHERNET
AVR_FIO
AVR_GEMMA
AVR_LEONARDO
AVR_LILYPAD
AVR_LILYPAD_USB
AVR_MEGA
AVR_MEGA2560
AVR_MICRO
AVR_MINI
AVR_NANO
AVR_NG
AVR_PRO
AVR_ROBOT_CONTROL
AVR_ROBOT_MOTOR
AVR_UNO
AVR_YUN
both: pinMode(13,OUTPUT);
uno mega
if (pRun->nr == 0)
PORTB = PORTB | B00100000; // led on uno
| B10000000; // mega
else
PORTB = PORTB & B11011111; // led off uno
& B01111111; / mega
mega
*/
/*
Suggested PORTS TO USE
UNO
pin port
8 PB0
9 PB1
10 PB2
11 PB3
12 PB4
13 PB5 LED13
PB6 and 7 etc not to be used !
MEGA
pin port
22 PA0
23 PA1
24 PA2
25 PA3
26 PA4
27 PA5
38 PA6
29 PA7
13 PB7 LED13
MICRO
8 PB4
9 PB5
10 PB6
11 PB7
13 PC7 LED13
NANO
D8 PD0
D9 PD1
D2 PD2
D3 PD3
D4 PD4
D5 PD5
D6 PD6
D7 PD7
13 PB5 LED13
PRO MINI
2 PD2
3 PD3
4 PD4
5 PD5
6 PD6
13 PB5 LED13
* */
Who is running
Idea: We will switch on a LED acc to task number
We know running task number can be found by pRun->nr
dummy has number 0
There is no other tasks inside krnl
Task are numbered in the order they are created eg 1,2,3,…
In the example for the UNO we take pins 8,9,…,13 for identifying tasks, so dmy is pin 8, task 1 is pin 9 etc
(click for code)
Pins 8-13 are on PORTB as seen above:
uno
pin port
8 PB0
9 PB1
10 PB2
11 PB3
12 PB4
13 PB5 (led)
PB6 and 7 etc not to be used !
So you just add
void setup()
{
uint8_t pp:
for (int i=8; i < 14; i++)
pinMode(i,OUTPUT);
// switch 8-13 OFF or low (qg bit 0 to 5
pp = PORTB & B11000000;
PORTB = pp;
}
extern "C" {
void k_breakout() // called every task shift from dispatcher
{
uint8t p;
p = (PORTB & B11000000) | ((0x01 << pRun->nr); //We assume we have max 5 user tasks
PORTB = p;
}
}
// 0x01 << pRun->nr moves a 1 up on a bit pos given by task nr. So task 4 (number 4 created) will set a 1 on bit 4
LEDs
Forgot to say …
Logic analyzer
Instead of using LEDs you can attach a logic analyzer to the pins used to identify who is running.
As see below you can get an excellent overview over who is running and when - aka scheduling :-)
LED 13 when dummy is running
The following shows how you can use compiler directive to find out which arduino you have
The example is only coded for mega mega2560 and uno but can easily be extended
(click for code)
#include <krnl.h>
// LED13 on i dummy is runningg
struct k_t *p1, *p2, *p3;
char st1[100], st2[100], st3[100];
// LED 13 not used from user space bq we use led for indicating dummy i srunning
// when dummy is running it indicates enough cpu power
// if dummy is not running for a longer time you may have problems ....
void t1()
{
while (1) {
Serial.print(pRun->nr);
Serial.println(" I am running");
k_eat_ticks(500); // you are running
Serial.println("dummy is now running");
k_sleep(2000); // you are NOT running so dummmy is running
}
}
void setup()
{
Serial.begin(9600);
pinMode(13, OUTPUT); // for debug
k_init(3, 0, 0); // init with space for three tasks
// priority low number higher priority than higher number
p1 = k_crt_task(t1, 10, st1, 100); // t1 as task, priority 10, 100 B stak
p2 = k_crt_task(t1, 11, st2, 100); // t1 as task, priority 10, 100 B stak
p3 = k_crt_task(t1, 12, st3, 100); // t1 as task, priority 10, 100 B stak
k_start(1); // 1 milli sec tick speed
}
void loop() {}
/*
** JUST FOR SHOWING YOU CAN TEST FOR CPU TYPE ....
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
#elif defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__) || defined(__AVR_ATmega2561__)
#endif
OR FOR BOARD TYPE SEE BELOW
*/
extern "C" {
void k_breakout() // called every task shift from dispatcher
{
// https://arduino.stackexchange.com/questions/19892/list-of-arduino-board-preprocessor-defines
// FOR UNO
#if defined (ARDUINO_AVR_UNO)
if (pRun->nr == 0)
{
PORTB = PORTB | B00100000; // led13 (bit 5) on let the rest be untouched
}
else {
PORTB = PORTB & B11011111; // led off uno
}
#endif
#if ( defined (ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) )
if (pRun->nr == 0)
{
PORTB = PORTB | B10000000; // led13 (bit 7) on let the rest be untouched
}
else {
PORTB = PORTB & B01111111; // led off mega
}
#endif
}
}
/* or just LED13 if dummy ...
YOu can test for board by #ifdef ARDUINO_AVR_UNO etc
see list below
Just add ARDUINO_ in front of
So an uno can be identified by #ifdef ARDUINO_AVR_UNO ...
AVR_ADK
AVR_BT
AVR_DUEMILANOVE
AVR_ESPLORA
AVR_ETHERNET
AVR_FIO
AVR_GEMMA
AVR_LEONARDO
AVR_LILYPAD
AVR_LILYPAD_USB
AVR_MEGA
AVR_MEGA2560
AVR_MICRO
AVR_MINI
AVR_NANO
AVR_NG
AVR_PRO
AVR_ROBOT_CONTROL
AVR_ROBOT_MOTOR
AVR_UNO
AVR_YUN
both: pinMode(13,OUTPUT);
uno mega
if (pRun->nr == 0)
PORTB = PORTB | B00100000; // led on uno
| B10000000; // mega
else
PORTB = PORTB & B11011111; // led off uno
& B01111111; / mega
mega
*/
/*
Suggested PORTS TO USE
UNO
pin port
8 PB0
9 PB1
10 PB2
11 PB3
12 PB4
13 PB5 LED13
PB6 and 7 etc not to be used !
MEGA
pin port
78 PA0
77 PA1
76 PA2
75 PA3
74 PA4
73 PA5
72 PA6
71 PA7
13 PB7 LED13
If you want to use 8,9,10,..13 on the MEGA its
8
MICRO
8 PB4
9 PB5
10 PB6
11 PB7
13 PC7 LED13
NANO
D8 PD0
D9 PD1
D2 PD2
D3 PD3
D4 PD4
D5 PD5
D6 PD6
D7 PD7
13 PB5 LED13
PRO MINI
2 PD2
3 PD3
4 PD4
5 PD5
6 PD6
13 PB5 LED13
* */
A large example
(click for code)
#ifdef NEVER
e02 - std critical region
task2 will only enter region if it's free
About using a semaphore for conditional wait
k_wait(sem,0)
0: no timeout - we will wait until we get an signal
returns:
0: we got a signal, and we has been sleeping at the semaphore waiting for a signal
1: we got a signal that was already waiting at the semaphore. We has not been sleeping
k_wait(sem,10)
2nd parm can be 1,2,3,... etc. Using 1 is not recommended bq timeout will be 0-1 tick
because you might issue wait call just bef sys timer is ticking
10: 10 tick maximal waiting time at semaphore
returns:
0 : we got a signal, and we has been sleeping at the semaphore waiting for a signal
1 : we got a signal that was already waiting at the semaphore. We has not been sleeping
or -1 : we got a timeout after ... 10 ticks. So wait call has not been a success
k_wait(sem,-1)
m1: we will not wait for a signal. We will just eat one if already available
returns:
1 : we got a signal that was already waiting at the semaphore. We has not been sleeping
or -1 : no signal available - so wait call has not succeded
// res == 1 means there was a signal pending on the semaphore - so we was not waiting
// res == 0
// k_eat_time(5);
#endif
#include <krnl.h>
#define STK 150
#define TASKPRIO 10
char stak1[STK], stak2[STK];
struct k_t * pTask1, *pTask2, *sem1, *sem2;
void task1()
{
int res = 0;
while (1) {
k_eat_ticks(5);
k_sleep(5);
}
}
void task2()
{
int res;
while (1) {
k_eat_ticks(6);
k_sleep(16);
}
}
void setup() {
// for debugging - only on uno eq- If Mega etc use PORTA(which is pn 22,23,...)
// see http://kom.aau.dk/~jdn/edu/doc/arduino/mega.png
init_IO_for_debug();
Serial.begin(9600);
k_init(2, 1, 0); // 2 task, 1 semaphores, 0 messaegQueues */
pTask1 = k_crt_task(task1, TASKPRIO, stak1, STK);
pTask2 = k_crt_task(task2, TASKPRIO , stak2, STK);
sem1 = k_crt_sem(1, 10); // 1: start value, 10: max value (clipping)
k_start(1); /* start krnl timer speed 1 milliseconds*/
Serial.println("If you see this then krnl didnt start :-( ");
}
void loop() {}
/*****************************************/
void init_IO_for_debug()
{
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
for (int i = 8; i < 8+6; i++)
pinMode(i, OUTPUT); // PORTB
#elif defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__) || defined(__AVR_ATmega2561__)
for (int i = 22; i < 22+6; i++)
pinMode(i, OUTPUT); // PORTA
#endif
}
/**********************************************************************/
/***** DEBUGGING PART - LED ON 8-12**********/
/************************ DEBUG CALLBACK BREAKOUT PART ****************/
// must be extern C ! its not mandatory to supply with these functions - only if you need
extern "C" {
// called when a semphore is clipping - nr is id of semaphore and i os nr of times clip has occured
unsigned char led13;
void k_sem_clip(unsigned char nr, int i)
{
return; // !!!!
if (nr == 2)
led13 |= 0x20;
}
void k_sem_unclip(unsigned char nr)
{
return; /// !!!
if (nr == 2)
led13 = 0x00;
}
// task numbering is in creation order: dummy: 0, first of yours 1, next 2,...
void k_breakout() // called every task shift from dispatcher
{
unsigned char c;
// if we do have a uno,nano etc use PORTB directly
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__)
PORTB = (1 << pRun->nr) | led13; // arduino uno !! specific usage of PORTB
#elif defined (__AVR_ATmega2560__) || defined (__AVR_ATmega1280__) || defined(__AVR_ATmega2561__
PORTA = (1 << pRun->nr); // | led13; // arduino mega !! specific usage of PORTA
#endif
}
// PORTS: get inspired at http://kom.aau.dk/~jdn/edu/doc/arduino/ards.html
}
And an analyzer:
DIO 0: dummy task .- high when it is running
DIO 1: task nr 1 - function task1
DIO 1: task nr 2 - function task2
Observe when task1 has highest priority it gets the 5 tick eating time as once, a
and therefore task2 might become dalyed
When they are equal they multitask for sharing cpu and they are both delayed a bit
First image equal priority
Second image task1 higher priority than task2.
Equal priority for task1 and task2 (DIO1 and DIO 2)
D0 is dummy task
Task1 (DIO 1) higher priority than task2(DIO 2)
D0 is dummy task
Suggested pins for LED debug
When using pins for debug by breakout from krnl its wise NOT to use digitalWrite functions because they are so so slow.
Instead direct port access is preferred. For that reason it is wise to use pins for the LEDs on the same port
Example code for an UNO
From the overview we will use PORTB bit 0 to 5 - remember bit 5 is the famous LED 13.
// FOR PORTB
void setup() {
for (int i = 8; i < 14; i++)
pinMode(i, OUTPUT);
// Set 8-13 LOW and preserve the rest
// 8-13 equals bit 0 to 5 so dont touch bit 6,7
PORTB = PORTB & B11000000; // = 0XC0 = 128+64
// portn bit 76543210
}
void setAllPins0To5(uint8_t bits)
{
bits = bits | B11000000; // Set bit 6,7 mandatory high
PORTB = PORTB & bits;
}
void setSingleBit(uint8_t bitnr, uint8_t highLow)
{
uint8_t b;
if (5 < bitnr)
return; // Error PORTB 6 and 7 is not yours
b = 0x01 << bitnr;
if (highLow) {
PORTB = PORTB | b;
}
else {
b = ! b; // invert
PORTB = PORTB & b;
}
}
void loop() {
// put your main code here, to run repeatedly:
setAllPins0To5(B00000101); // set PORTB 0 and 2 high and rest low
setSingleBit(4, 1);
setSingleBit(3, 0);
delay(1000);
setSingleBit(4, 0);
setSingleBit(3, 1);
delay(1000);
}
|