3.1. Blinky demo¶
The ‘blinky’ demo is in the directory app/blinky
.
3.1.1. Building and running the demo¶
To build a new firmware and to flash the board with it, follow the instructions in Quick start.
To choose the ‘blinky’ profile, you will have to execute make
with
$ make boards/32f407disco/configs/disco_blinky_ada_defconfig
3.1.2. Understanding the demo code¶
3.1.2.1. LEDs configuration¶
To use a device, first, it must be declared and registered in the kernel.
A device_t
structure is associated to the LEDs. It describes its memory
mapping, the GPIOs and the EXTIs of the device.
device_t leds;
...
memset (&leds, 0, sizeof (leds));
strncpy (leds.name, "LEDs", sizeof (leds.name));
leds.gpio_num = 4; /* Number of configured GPIO */
leds.gpios[0].kref.port = GPIO_PD;
leds.gpios[0].kref.pin = 12;
leds.gpios[0].mask = GPIO_MASK_SET_MODE | GPIO_MASK_SET_PUPD |
GPIO_MASK_SET_TYPE | GPIO_MASK_SET_SPEED;
leds.gpios[0].mode = GPIO_PIN_OUTPUT_MODE;
leds.gpios[0].pupd = GPIO_PULLDOWN;
leds.gpios[0].type = GPIO_PIN_OTYPER_PP;
leds.gpios[0].speed = GPIO_PIN_HIGH_SPEED;
leds.gpios[1].kref.port = GPIO_PD;
leds.gpios[1].kref.pin = 13;
leds.gpios[1].mask = GPIO_MASK_SET_MODE | GPIO_MASK_SET_PUPD |
GPIO_MASK_SET_TYPE | GPIO_MASK_SET_SPEED;
...
Then, the sys_init()
syscall is used to register the device with the kernel:
ret = sys_init(INIT_DEVACCESS, &leds, &desc_leds);
The desc_leds
variable is a descriptor returned by the syscall. It’s
ignored in that example (it’s used only by some few syscalls sys_cfg, configuring devices).
3.1.2.2. Button configuration¶
The button must also be declared and registered with the kernel. Note the registration of the ISR handler with:
button.gpios[0].exti_trigger = GPIO_EXTI_TRIGGER_RISE;
button.gpios[0].exti_lock = GPIO_EXTI_UNLOCKED;
button.gpios[0].exti_handler = (user_handler_t) exti_button_handler;
The GPIO_EXTI_TRIGGER_RISE
configures the IRQ associated to the GPIO to be
triggered only on a rising edge (corresponding to a button push in our case).
The GPIO_EXTI_UNLOCKED
has a special meaning. The GPIO_EXTI_LOCKED
means that the EXTI line is muted and that it must be voluntary “unlocked”
using a specific syscall to receive further EXTIs.
The exti_handler
field is initialized with the address of the ISR handler
that will be executed for each EXTI interrupt.
3.1.2.3. Leaving the initialization phase¶
When all devices are registered, they still can’t be used by the app.
Before, the initialization phase must be leaved using the
sys_init(INIT_DONE)
syscall before using them:
/* Devices and resources registration is finished */
ret = sys_init(INIT_DONE);
Be aware that after that, no more further device or resource registration is possible.
3.1.2.4. ISR handler¶
In our example, the ISR handler exti_button_handler()
set the global variable button_pushed
to notify the interrupt event:
void exti_button_handler ()
{
uint64_t clock;
e_syscall_ret ret;
/* Syscall to get the elapsed cpu time since the board booted */
ret = sys_get_systick(&clock, PREC_MILLI);
if (ret == SYS_E_DONE) {
/* Debounce time (in ms) */
if (clock - last_isr < 20) {
last_isr = clock;
return;
}
}
last_isr = clock;
button_pushed = true;
}
The only subtlety here is the debouncing handling inside the ISR to avoid
burst of interrupts.
The debouncing time is arbitrary fixed to 20 milliseconds.
The sys_get_systick()
syscall is used to return elapsed CPU time since the
board booted.
3.1.2.5. Main loop¶
After the initialization phase, the main function executes a loop that waits
for interrupt notifications by checking the value of button_pushed
.
When the Button is pushed, LEDs blinking pattern is switched.
while (1) {
if (button_pushed == true) {
printf ("button has been pressed\n");
/* Change leds state */
green_state = (green_state == ON) ? OFF : ON;
orange_state = (orange_state == ON) ? OFF : ON;
red_state = (red_state == ON) ? OFF : ON;
blue_state = (blue_state == ON) ? OFF : ON;
/* Show leds */
display_leds = ON;
button_pushed = false;
}
...
To make the LEDs blinking, their related GPIO must be set to ON of OFF
using the sys_cfg()
syscall:
....
if (display_leds == ON) {
ret = sys_cfg(CFG_GPIO_SET, (uint8_t) leds.gpios[0].kref.val, green_state);
if (ret != SYS_E_DONE) {
printf ("sys_cfg(): failed\n");
return 1;
}
...
} else {
ret = sys_cfg(CFG_GPIO_SET, (uint8_t) leds.gpios[0].kref.val, 0);
if (ret != SYS_E_DONE) {
printf ("sys_cfg(): failed\n");
return 1;
}
...
Then, the task sleeps 500 milliseconds:
/* Sleeping for 500 ms */
sys_sleep (500, SLEEP_MODE_INTERRUPTIBLE);
If the button is pushed during that sleeping time, the task is awake
due to the SLEEP_MODE_INTERRUPTIBLE
option.