Event Queue
In the two step process described at the Part 1, short and fast process should transfer information of certain event to the long and slow process. This can be done simply by raising a flag. Alternatively it can be done more systematically by using a data structure such as a ring buffer.
You can create a ring buffer whose element size is few byte long and use it as a queue to convey short information about events. Let us call it an event queue. As a minimum, you need two functions, put and get (or enqueue and dequeue).
66 #define EVT_QDEPTH (8) 67 /** The maximum size of the event data. It consists of one byte of event code 68 * with variable length of data bytes. 69 */ 70 #define EVT_QWIDTH (16) 71 72 /// Register a new event 73 bool Evt_EnQueue(uint8_t *event); 74 /// Checkout the oldest event 75 bool Evt_DeQueue(uint8_t *event); 76 /// Initialize the event queue 77 void Evt_InitQueue(void);
As for the usage, for the sake of argument, let us the first byte of a queue element denote the category of event and the next byte detailed type. Then we can define event data structure for pushbutton handler like below.
4 /** Pushbutton input event 5 * 6 * Event Data: (EVT_SRC)(EVT_TYPE) 7 * 8 * * EVT_SRC: id of the pushbutton that generated the event 9 * * EVT_TYPE: type of the event such as single click, double click, 10 */ 11 #define PBTN_INPUT 0x10 ///< event Source: pushbutton input 12 13 #define PBTN_SCLK 0x01 ///< event Type: single click 14 #define PBTN_LCLK 0x02 ///< event Type: long click 15 #define PBTN_DCLK 0x03 ///< event Type: double click 16 #define PBTN_TCLK 0x04 ///< event Type: triple click 17 #define PBTN_DOWN 0x05 ///< event Type: button state down 18 #define PBTN_ENDN 0x06 ///< event Type: button state changed to up
When event is detected by the fast process (PushButton_Routine), it pushes relevant information to the queue.
112 // up-down mode 113 if(((pp.mode >> i) & 0x01) == PUSHBTN_MODE_UDOWN) 114 { 115 // the button pressed 116 if(((pp.new_state >> i) & 0x01) == 0x01) 117 { 118 event[0] = EVT_PBTN_INPUT; 119 event[1] = (uint8_t)(i+1); 120 event[2] = PBTN_DOWN; 121 122 // post the event as long as the button is pressed down 123 Evt_EnQueue(event);In the main loop, event handler (slow process) keeps checking the queue and processes it whenever new event is posted.
115 while (1) 116 { 117 /* USER CODE END WHILE */ 118 119 /* USER CODE BEGIN 3 */ 120 121 // check event queue 122 if(Evt_DeQueue(event)) 123 { 124 switch(event[0]) 125 { 126 // pushbutton event ================================================ 127 // event[1]: button id 128 // event[2]: PBTN_SCLK, _DCLK, _TCLK, _LCLK, _DOWN, _ENDN 129 case EVT_PBTN_INPUT: 130 131 if(event[2] == PBTN_SCLK) 132 { 133 UartPrintf("\r\nButton %d: single click.", event[1]); 134 }
(timing consideration) Since the software timer is based on 1 msec SysTick, each callback should do its job much faster than 1 msec, preferably within a few millisecond. So slow routines such as ADC conversion or SPI transaction should be strictly avoided.
(race condition) The event queue is shared by the slow processes and the fast processes, where the fast processes preempt the slow processes since they are the callback of SysTick interrupt. To avoid race condition, Software timer should be paused when slow process access the queue.
63 bool Evt_DeQueue(uint8_t *event) 64 { 65 uint8_t i; 66 bool flag = false; 67 68 // disable all timers 69 UsrTimer_Enable(false); 70 71 // queue is not empty 72 if(evt_queue.tail != evt_queue.head) 73 { 74 // copy event bytes into the buffer 75 for(i = 0; i < EVT_QWIDTH; i++) 76 { 77 event[i] = evt_queue.buff[evt_queue.tail][i]; 78 } 79 // move to the next position 80 evt_queue.tail = ADVANCE_QPTR(evt_queue.tail); 81 // set flag 82 flag = true; 83 } 84 85 // enable all timers 86 UsrTimer_Enable(true); 87 88 // return with the flag 89 return flag; 90 }