Appiko
led_ui.c
1 
29 #include "led_ui.h"
30 #include "hal_pwm.h"
31 #include "nrf_util.h"
32 #include "log.h"
33 #include "boards.h"
34 #include "stddef.h"
35 
36 #define MAX_COUNT_PWM 1000
37 #define MAX_SEQ_LEN_SEC 20
38 #define PWM_UPDATE_PERIOD_MS 32
39 #define PWM_BUFFER_SIZE ((uint32_t)((MAX_SEQ_LEN_SEC*1000)/PWM_UPDATE_PERIOD_MS))
40 
41 static struct{
42  led_sequences seq;
43  bool is_on;
44  uint16_t priority;
45 }led_ui_context[LED_UI_SEQ_T_SIZE];
46 
47 static struct
48 {
49  uint16_t color[LED_COLOR_MAX];
50 } seq_buffer[PWM_BUFFER_SIZE];
51 
52 const uint16_t zero_val_arr[32] = { 0 };
53 
54 void pwm_irq_handler(hal_pwm_irq_mask_t irq_source)
55 {
56  if(HAL_PWM_IRQ_STOPPED_MASK == irq_source)
57  {
58  led_ui_context[LED_UI_SINGLE_SEQ].is_on = false;
59  if(led_ui_context[LED_UI_LOOP_SEQ].is_on == true)
60  {
61  led_ui_loop_start(led_ui_context[LED_UI_LOOP_SEQ].seq,
62  led_ui_context[LED_UI_LOOP_SEQ].priority);
63  }
64  }
65 }
66 
67 static bool check_single_seq(led_sequences seq, led_ui_priority_t priority, bool reset)
68 {
69  //If the same single sequence is running and reset is false
70  if((led_ui_context[LED_UI_SINGLE_SEQ].seq == seq) &&
71  (led_ui_context[LED_UI_SINGLE_SEQ].is_on == true) && (reset == false))
72  {
73  return true;
74  }
75 
76  //If a higher priority single sequence is running
77  if((led_ui_context[LED_UI_SINGLE_SEQ].priority > priority)
78  && (led_ui_context[LED_UI_SINGLE_SEQ].is_on == true))
79  {
80  return true;
81  }
82 
83  //If a higher priority loop sequence is running
84  if((led_ui_context[LED_UI_LOOP_SEQ].priority > priority)
85  && (led_ui_context[LED_UI_LOOP_SEQ].is_on == true))
86  {
87  return true;
88  }
89 
90  //Otherwise start the single sequence
91  return false;
92 }
93 
94 static bool check_loop_seq(led_sequences seq, led_ui_priority_t priority)
95 {
96  //A higher priority single sequence is running
97  if((led_ui_context[LED_UI_SINGLE_SEQ].is_on == true) &&
98  (led_ui_context[LED_UI_SINGLE_SEQ].priority >= priority))
99  {
100  //Either loop sequence is not set or the set one is of lower priority
101  if((led_ui_context[LED_UI_LOOP_SEQ].is_on == false) ||
102  ((led_ui_context[LED_UI_LOOP_SEQ].priority < priority) &&
103  (led_ui_context[LED_UI_LOOP_SEQ].is_on == true)))
104  {
105  led_ui_context[LED_UI_LOOP_SEQ].is_on = true;
106  led_ui_context[LED_UI_LOOP_SEQ].seq = seq;
107  led_ui_context[LED_UI_LOOP_SEQ].priority = priority;
108  }
109  return true;
110  }
111 
112  //A higher priority loop sequence is running
113  if((led_ui_context[LED_UI_LOOP_SEQ].priority > priority)
114  && (led_ui_context[LED_UI_LOOP_SEQ].is_on == true))
115  {
116  return true;
117  }
118 
119  //In case a lower priority single seq is usurped, take care of that
120  if((led_ui_context[LED_UI_SINGLE_SEQ].is_on == true) &&
121  (led_ui_context[LED_UI_SINGLE_SEQ].priority < priority))
122  {
123  led_ui_context[LED_UI_SINGLE_SEQ].is_on = false;
124  }
125  return false;
126 }
127 
128 static void start_seq_pwm(led_sequences seq, led_ui_seq_t type, led_ui_priority_t priority)
129 {
130  led_ui_context[type].is_on = true;
131  led_ui_context[type].seq = seq;
132  led_ui_context[type].priority = priority;
133 
134  uint16_t * dur_ptr = led_seq_get_seq_duration_ptr(seq);
135  uint32_t seq_seg_num = led_seq_get_seg_len(seq);
136 
137  uint32_t led_num = led_seq_get_pin_num(seq);
138 
139  uint16_t * seq_ptr[LED_COLOR_MAX];
140  uint32_t pin_arr[LED_COLOR_MAX];
141  bool pin_idle[LED_COLOR_MAX];
142 
143  for(uint32_t k = 0; k < led_num; k++)
144  {
145  seq_ptr[k] = led_seq_get_seq_color_ptr(seq, k);
146  pin_arr[k] = led_seq_get_pin_ptr()[k];
147  pin_idle[k] = (!LEDS_ACTIVE_STATE);
148  }
149  for(uint32_t k = led_num; k < LED_COLOR_MAX; k++)
150  {
151  seq_ptr[k] = (uint16_t *)zero_val_arr;
152  pin_arr[k] = led_seq_get_pin_ptr()[0];
153  pin_idle[k] = (!LEDS_ACTIVE_STATE);
154  }
155 
156  uint32_t overflow = 0; //To store the overflow from one segment to another
157  uint32_t buff_cnt = 0; //To store the number of elements generated in buffer
158 
159  for(uint32_t i = 1; i<seq_seg_num; i++)
160  {
161  uint32_t curr_seg_dur = dur_ptr[i] - overflow;
162  uint32_t seg_updates_num = 1 + curr_seg_dur/PWM_UPDATE_PERIOD_MS;
163 
164  for(uint32_t j = 0; j < seg_updates_num; j++)
165  {
166  uint32_t seg_count = overflow + j*PWM_UPDATE_PERIOD_MS;
167  for(uint32_t k = 0; k < LED_COLOR_MAX; k++)
168  {
169  seq_buffer[buff_cnt].color[k] = ((int32_t)(seg_count *
170  ((int32_t)(seq_ptr[k][i] - seq_ptr[k][i-1]))))
171  /dur_ptr[i] + seq_ptr[k][i-1];
172 
173  seq_buffer[buff_cnt].color[k] = (LEDS_ACTIVE_STATE)?
174  (seq_buffer[buff_cnt].color[k] | (1<<15)):
175  (seq_buffer[buff_cnt].color[k]);
176  }
177  buff_cnt++;
178  }
179 
180  uint32_t curr_dur_mod = curr_seg_dur - (seg_updates_num-1)*PWM_UPDATE_PERIOD_MS;
181  overflow = (curr_dur_mod)?(PWM_UPDATE_PERIOD_MS - curr_dur_mod):0;
182  }
183  buff_cnt--;
184 
185  hal_pwm_init_t init_config =
186  {
187  .pins = pin_arr,
188  .pin_idle_state = pin_idle,
189  .pin_num = LED_COLOR_MAX,
190  .oper_freq = HAL_PWM_FREQ_1MHz,
191  .oper_mode = HAL_PWM_MODE_UP,
192  .irq_priority = APP_IRQ_PRIORITY_MID
193  };
194  hal_pwm_init(&init_config);
195 
196  hal_pwm_start_t start_config =
197  {
198  .countertop = MAX_COUNT_PWM,
199  .decoder_load = HAL_PWM_LOAD_GROUPED,
200  .decoder_trigger = HAL_PWM_STEP_INTERNAL,
201  .seq_config =
202  {
203  {
204  .seq_values = (uint16_t *) seq_buffer,
205  .len = (LED_COLOR_MAX * buff_cnt),
206  .repeats = (PWM_UPDATE_PERIOD_MS - 1),
207  .end_delay = 0
208  },
209  {
210  .seq_values = (uint16_t *) seq_buffer,
211  .len = (LED_COLOR_MAX * buff_cnt),
212  .repeats = (PWM_UPDATE_PERIOD_MS - 1),
213  .end_delay = 0
214  }
215  },
216  };
217 
218  if(type == LED_UI_SINGLE_SEQ)
219  {
220  start_config.loop = 0;
223  start_config.irq_handler = pwm_irq_handler;
224  }
225  else // LOOP_SEQ
226  {
227  start_config.loop = 1;
229  start_config.interrupt_masks = 0;
230  start_config.irq_handler = NULL;
231  }
232 
233  hal_pwm_start(&start_config);
234 }
235 
236 void led_ui_single_start(led_sequences seq, led_ui_priority_t priority, bool reset)
237 {
238 
239  if(check_single_seq(seq, priority, reset))
240  {
241  return;
242  }
243 
244  start_seq_pwm(seq, LED_UI_SINGLE_SEQ, priority);
245 }
246 
247 void led_ui_loop_start(led_sequences seq, led_ui_priority_t priority)
248 {
249  if(check_loop_seq(seq, priority))
250  {
251  return;
252  }
253 
254  start_seq_pwm(seq, LED_UI_LOOP_SEQ, priority);
255 }
256 
257 static void stop_seq_type(led_ui_seq_t type)
258 {
259  //In case where single is running and loop needs to stop, don't call pwm_stop
260  if((LED_UI_SINGLE_SEQ == type) || ((LED_UI_LOOP_SEQ == type)
261  && (led_ui_context[LED_UI_SINGLE_SEQ].is_on == false)))
262  {
263  hal_pwm_stop();
264  }
265  led_ui_context[type].is_on = false;
266 
267  //This condition will only be true for type == LED_UI_SINGLE_SEQ
268  if(led_ui_context[LED_UI_LOOP_SEQ].is_on == true)
269  {
270  led_ui_loop_start(led_ui_context[LED_UI_LOOP_SEQ].seq,
271  led_ui_context[LED_UI_LOOP_SEQ].priority);
272  }
273 }
274 
276 {
277  if(led_ui_context[type].is_on == true)
278  {
279  stop_seq_type(type);
280  }
281 }
282 
283 void led_ui_stop_seq(led_ui_seq_t type, led_sequences seq)
284 {
285  if((led_ui_context[type].is_on == true) &&
286  (led_ui_context[type].seq == seq))
287  {
288  stop_seq_type(type);
289  }
290 }
291 
292 void led_ui_stop_priority(led_ui_seq_t type, uint32_t priority)
293 {
294  if((led_ui_context[type].is_on == true) &&
295  (led_ui_context[type].priority == priority))
296  {
297  stop_seq_type(type);
298  }
299 }
300 
302 {
303  hal_pwm_stop();
304  led_ui_context[LED_UI_SINGLE_SEQ].is_on = false;
305  led_ui_context[LED_UI_LOOP_SEQ].is_on = false;
306 }
307 
309 {
310  return led_ui_context[type].seq;
311 }
312 
uint32_t shorts_mask
Select the shortcuts that need to be enabled. OR the values in hal_pwm_short_mask_t to enable them.
Definition: hal_pwm.h:191
Short between LOOPSDONE event and SEQSTART[0] task.
Definition: hal_pwm.h:86
hal_pwm_irq_mask_t
PWM interrupts.
Definition: hal_pwm.h:142
uint32_t * pins
Pointer to array containing the pins number used by hal pwm.
Definition: hal_pwm.h:166
Run a sequence only once.
Definition: led_ui.h:51
uint32_t interrupt_masks
Select the interrupts that need to be enabled OR the values in hal_pwm_irq_mask_t to enable them.
Definition: hal_pwm.h:194
After the loaded value is repeated for the number of times in REFRESH register.
Definition: hal_pwm.h:112
uint32_t countertop
Maximum count of the counter. This and oper_freq decide the frequency of the resulting PWM waveform.
Definition: hal_pwm.h:186
void led_ui_loop_start(led_sequences seq, led_ui_priority_t priority)
Start a sequence to play repeatedly.
Definition: led_ui.c:247
#define LEDS_ACTIVE_STATE
Definition: bluey_1v1.h:44
void led_ui_single_start(led_sequences seq, led_ui_priority_t priority, bool reset)
Start a sequence to play once.
Definition: led_ui.c:236
led_sequences led_ui_get_current_seq(led_ui_seq_t type)
Get the currently active LED UI sequence.
Definition: led_ui.c:308
16 MHz / 8 = 2 MHz.
Definition: hal_pwm.h:60
void hal_pwm_start(hal_pwm_start_t *start_config)
Start the PWM generation based on the configuration provided.
Definition: hal_pwm.c:192
Up counter, reset to 0 on incrementing to CounterTop.
Definition: hal_pwm.h:70
void led_ui_stop_everything(void)
Stops all LED sequences running.
Definition: led_ui.c:301
void led_ui_type_stop_all(led_ui_seq_t type)
Stop all sequence of a particular type.
Definition: led_ui.c:275
Run a sequence repeatedly.
Definition: led_ui.h:50
uint32_t loop
The number of times the pattern of both seq_config must be repeated.
Definition: hal_pwm.h:188
void hal_pwm_stop(void)
Stop the PWM generation.
Definition: hal_pwm.c:229
Short between SEQEND[0] event and STOP task.
Definition: hal_pwm.h:82
Interrupt on STOPPED event.
Definition: hal_pwm.h:145
Struct for initializing the hal pwm module.
Definition: hal_pwm.h:163
led_ui_seq_t
To specify if a sequence is run once or repeatedly.
Definition: led_ui.h:48
void hal_pwm_init(hal_pwm_init_t *init_config)
Initialize the HAL PWM module. Can be called again to change the initialization configuration.
Definition: hal_pwm.c:126
1st 16-bit value used in channels 0 and 1; 2nd one in channels 2 and 3.
Definition: hal_pwm.h:100
void led_ui_stop_priority(led_ui_seq_t type, uint32_t priority)
Stops LED sequences of a particular type and of a priority level or less.
Definition: led_ui.c:292
Struct containing the configuration for starting the hal pwm module.
Definition: hal_pwm.h:182
led_ui_priority_t
The LED sequence priority levels.
Definition: led_ui.h:40
void(* irq_handler)(hal_pwm_irq_mask_t irq_source)
the handler called based on the interrupt generated with the argument providing the source of the int...
Definition: hal_pwm.h:206
void led_ui_stop_seq(led_ui_seq_t type, led_sequences seq)
Stops a particular sequence of a particular type.
Definition: led_ui.c:283