Assignments for session 5


#1

Greetings all,

We’ll post the presentation for session 5 soon. The assignment for session 5 is to implement the state machine that we did yesterday. Here’s the state machine:

Please implement it as shown yesterday. Let us know if you have any questions.

Also please form teams of three and let us know your ideas. And do this by Tuesday so that the ideas can be reviewed.

Cheers!


#2

Here’s the presentation from session 5.


#3

Here is the code that i attempted on state transitions.

  1. It is wip. I haven’t written code for timer reset on green. I wanted to check whether the overall use of function pointers is correct

  2. It seems to work for these transitions
    (a) Off -> Red (on button click)
    Red -> off (on timer)

(b) Red -> Green (on button click)
Green -> Red (on timer)
Red -> Off (on timer)
3) There is however one strange behavior. The application doesn’t work if i comment a print statement in the button_press() method. I have marked it as //Doubt1 in the code. Obviously that’s not the real reason, and i am doing some other silly thing but i am too zoomed in to figure it out.
4) And hopefully my indentation attempt works !

// Initial Declarations
typedef enum  {
	OFF_STATE,
	RED_STATE,
	GREEN_STATE,
	NUM_STATES
} machine_state;

typedef void (*ptr_process_state_function)();
ptr_process_state_function process_states[3];

machine_state curr_state = OFF_STATE; 
void all_off();

void rgb_led_init(void)
{
	tfp_printf("Set all LEDs to inactive !\n");
	hal_gpio_cfg_output(LED_RED, !(LEDS_ACTIVE_STATE));
	hal_gpio_cfg_output(LED_GREEN, !(LEDS_ACTIVE_STATE));
	hal_gpio_cfg_output(LED_BLUE,!(LEDS_ACTIVE_STATE));
}

void process_off(void)
{
	tfp_printf("Set red LED to active !\n");
	rgb_led_init();    
	hal_gpio_cfg_output(LED_RED, (LEDS_ACTIVE_STATE));
	curr_state = RED_STATE;
	ms_timer_start(MS_TIMER0, MS_REPEATED_CALL, RTC_TICKS_MS(5000), all_off);
}


void process_red(void)
{
	tfp_printf("Set green LED to active !\n");
	rgb_led_init();    
	hal_gpio_cfg_output(LED_GREEN, (LEDS_ACTIVE_STATE));
	curr_state = GREEN_STATE;
	ms_timer_start(MS_TIMER0, MS_REPEATED_CALL, RTC_TICKS_MS(5000), process_off);
}

void process_green(void)
{
	tfp_printf("Staying with green LED to active !\n");
	rgb_led_init();    
	hal_gpio_cfg_output(LED_GREEN, (LEDS_ACTIVE_STATE));
	curr_state = GREEN_STATE;
	ms_timer_start(MS_TIMER0, MS_REPEATED_CALL, RTC_TICKS_MS(5000), process_green_extend);
}

void all_off(void) {
	curr_state = OFF_STATE;
	tfp_printf("Timer went off \n");
	rgb_led_init();
	return;
}

void button_press(void) {
	// read button press value    
	int button_value = hal_gpio_pin_read(BUTTON_1);

	static int prev_button_value = 1;	

	//	tfp_printf("Prev Button Value: %d !\n", prev_button_value);
	//	tfp_printf("Button Value: %d !\n", button_value);
	
	// Doubt 1:
	tfp_printf("Current State: %d !\n", curr_state);


	// if the button was released now and was earlier pressed
	if (prev_button_value == BUTTONS_ACTIVE_STATE && 
	button_value != BUTTONS_ACTIVE_STATE ) {

		tfp_printf("Button pressed !\n");
		(process_states[curr_state]());
	}

	prev_button_value = button_value;
	return; 	 		
}

int main() {
	tfp_printf("Testing State Transition \n");

	uart_printf_init(UART_PRINTF_BAUD_9600);
	hal_gpio_cfg_input(BUTTON_1, HAL_GPIO_PULL_DISABLED);

	hfclk_xtal_init_blocking();
	lfclk_init(LFCLK_SRC_Xtal);

	rgb_led_init();
	ms_timer_init(APP_IRQ_PRIORITY_HIGH);

	process_states[0] = &process_off;
	process_states[1] = &process_red;
	process_states[2] = &process_green;

	while (true) {
		button_press();
		__WFI();
	}
	return 0;
}

#4

Hi Sree, I can see this strange behaviour that you’re pointing at. Can’t figure out why. I’ll give it a go when the sun is shining. Good stuff otherwise. The state machine is not exactly the way we mentioned, but hey, it kind of works.


#5

Can you paste the code that you wrote in the class perhaps towards the end of the week after everyone has had a chance to try on their own. That code “felt” better :slight_smile:


#6

Yes, will do by Friday. In the meanwhile please take a look at switch bouncing, which is relevant for what we’re doing here. In our case, we can get around in software by having a delay of about 10 ms so that we only sample the button state at 100 Hz.


#7

Hi Sree, figured out why the code was working only with the printf statement. It’s because of the __WFI(); in the while loop. This will make the processor go to sleep and wake it up on any interrupt (WFI = wake from interrupt). Since the uart printf module internally uses interrupts, it used to wake the processor up. So just remove this statement to make the processor just keep running the button_press() function repeatedly. One more thing, there should be no tfp_printf statements before initializing the uart module.

The reason the code in the session ‘felt’ better is because each of the process function did what each state had to do: check for the button and do the appropriate task in the while(1) loop. In your approach the button check is happening in while(1) and based on this you’re calling the appropriate process.

Also, there was an issue with the ms timer module where in a MS_SINGLE_CALL handler could not start the timer again. This has been fixed. Please pull from the repo to get the fix.


#8

Here’s the code that was done at the end of the previous session

#include <stdbool.h>
#include <stdint.h>

#include "nrf.h"

#include "boards.h"
#include "hal_clocks.h"
#include "hal_nop_delay.h"
#include "hal_gpio.h"
#include "common_util.h"
#include "tinyprintf.h"
#include "uart_printf.h"
#include "ms_timer.h"
#include "simple_pwm.h"
#include "nrf_util.h"

bool button_check(void);

/** @brief The three states of the state machine */
typedef enum
{
    OFF_STATE,
    RED_STATE,
    GREEN_STATE
} sm_states;

sm_states state = OFF_STATE;

typedef void(*state_process)(void);

/**
 * @brief 5s timeout handler used to go from green to red state or
 *  red to off state.
 */
void repeat_handler(void)
{
    switch(state)
    {
    case RED_STATE:
        state = OFF_STATE;
        hal_gpio_pin_write(LED_RED, !LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_GREEN, !LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_BLUE, !LEDS_ACTIVE_STATE);
        tfp_printf("RED to OFF\n");
        break;
    case GREEN_STATE:
        state = RED_STATE;
        hal_gpio_pin_write(LED_RED, LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_GREEN, !LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_BLUE, !LEDS_ACTIVE_STATE);
        ms_timer_start(MS_TIMER0, MS_SINGLE_CALL, RTC_TICKS_MS(5000), repeat_handler);
        tfp_printf("GREEN to RED\n");
        break;

    case OFF_STATE:
    default:
        break;
    }
}

/** @brief Green state process checks if there is a button press to reset the timer */
void green_process(void)
{
    bool btn_check = button_check();
    if(btn_check)
    {
        ms_timer_start(MS_TIMER0, MS_SINGLE_CALL, RTC_TICKS_MS(5000), repeat_handler);
    }
}

/** @brief Off state process checks if there is a button press to go to red state */
void off_process(void)
{
    bool btn_check = button_check();
    if(btn_check)
    {
        hal_gpio_pin_write(LED_RED, LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_GREEN, !LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_BLUE, !LEDS_ACTIVE_STATE);
        state = RED_STATE;
        ms_timer_start(MS_TIMER0, MS_SINGLE_CALL, RTC_TICKS_MS(5000), repeat_handler);
        tfp_printf("OFF to RED\n");
    }
}

/** @brief Red state process checks if there is a button press to go to green state */
void red_process(void)
{
    bool btn_check = button_check();
    if(btn_check)
    {
        hal_gpio_pin_write(LED_RED, !LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_GREEN, LEDS_ACTIVE_STATE);
        hal_gpio_pin_write(LED_BLUE, !LEDS_ACTIVE_STATE);
        state = GREEN_STATE;
        ms_timer_start(MS_TIMER0, MS_SINGLE_CALL, RTC_TICKS_MS(5000), repeat_handler);
        tfp_printf("RED to GREEN\n");
    }
}

/** @brief Configure the RGB LED pins as output and turn off LED */
static void rgb_led_init(void)
{
    hal_gpio_cfg_output(LED_RED, !(LEDS_ACTIVE_STATE));
    hal_gpio_cfg_output(LED_GREEN, !(LEDS_ACTIVE_STATE));
    hal_gpio_cfg_output(LED_BLUE, !(LEDS_ACTIVE_STATE));
}

/** @brief Cycle through the red, green and blue LEDs */
static void rgb_led_cycle(void)
{
    hal_gpio_pin_write(LED_RED, (LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_GREEN, !(LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_BLUE, !(LEDS_ACTIVE_STATE));
    hal_nop_delay_ms(250);
    hal_gpio_pin_write(LED_RED, !(LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_GREEN, (LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_BLUE, !(LEDS_ACTIVE_STATE));
    hal_nop_delay_ms(250);
    hal_gpio_pin_write(LED_RED, !(LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_GREEN, !(LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_BLUE, (LEDS_ACTIVE_STATE));
    hal_nop_delay_ms(250);
    hal_gpio_pin_write(LED_RED, !(LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_GREEN, !(LEDS_ACTIVE_STATE));
    hal_gpio_pin_write(LED_BLUE, !(LEDS_ACTIVE_STATE));
}

/**
 * @brief Function that returns if the button was released from the previous time
 *  it was called
 * @return True if a button release happened, else false
 */
bool button_check(void)
{
    static uint32_t prev_state = !BUTTONS_ACTIVE_STATE;
    bool falling_edge;
    uint32_t curr_state = hal_gpio_pin_read(BUTTON_1);
    if((curr_state == !BUTTONS_ACTIVE_STATE) && (prev_state == BUTTONS_ACTIVE_STATE))
    {
         falling_edge = true;
    }
    else
    {
        falling_edge = false;
    }
    prev_state = curr_state;
    return falling_edge;
}

/**
 * @brief Function for application main entry.
 */
int main(void)
{
    rgb_led_init();
    rgb_led_cycle();
    /* Initial printf */
    uart_printf_init(UART_PRINTF_BAUD_9600);

    tfp_printf("Hello World %d!\n", 1);

    hfclk_xtal_init_blocking();
    lfclk_init(LFCLK_SRC_Xtal);

    ms_timer_init(APP_IRQ_PRIORITY_MID);

    hal_gpio_cfg_input(BUTTON_1, HAL_GPIO_PULL_DISABLED);

    state_process process[3] =
    {
            &off_process,
            &red_process,
            &green_process
    };

    while (true)
    {
        process[state]();
        //To take care of button debouncing
        hal_nop_delay_ms(50);
    }
}