CuVoodoo STM32F1 firmware template
busvoodoo_uart_generic.c
Go to the documentation of this file.
1 /* This program is free software: you can redistribute it and/or modify
2  * it under the terms of the GNU General Public License as published by
3  * the Free Software Foundation, either version 3 of the License, or
4  * (at your option) any later version.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9  * GNU General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public License
12  * along with this program. If not, see <http://www.gnu.org/licenses/>.
13  *
14  */
21 /* standard libraries */
22 #include <stdint.h> // standard integer types
23 #include <stdlib.h> // standard utilities
24 #include <string.h> // string utilities
25 
26 /* STM32 (including CM3) libraries */
27 #include <libopencm3/cm3/nvic.h> // interrupt utilities
28 #include <libopencm3/stm32/gpio.h> // general purpose input output library
29 #include <libopencm3/stm32/rcc.h> // real-time control clock library
30 #include <libopencm3/stm32/usart.h> // USART utilities
31 #include <libopencm3/stm32/timer.h> // timer library
32 
33 /* own libraries */
34 #include "global.h" // board definitions
35 #include "print.h" // printing utilities
36 #include "interrupt.h" // user interrupt table
37 #include "menu.h" // menu definitions
38 #include "usart_enhanced.h" // utilities for USART enhancements
39 #include "busvoodoo_global.h" // BusVoodoo definitions
40 #include "busvoodoo_uart_generic.h" // own definitions
41 
46  BUSVOODOO_UART_SETTING_NONE,
47  BUSVOODOO_UART_SETTING_BAUDRATE,
48  BUSVOODOO_UART_SETTING_DATABITS,
49  BUSVOODOO_UART_SETTING_PARITY,
50  BUSVOODOO_UART_SETTING_STOPBITS,
51  BUSVOODOO_UART_SETTING_HWFLOWCTL,
52  BUSVOODOO_UART_SETTING_DRIVE,
53  BUSVOODOO_UART_SETTING_DONE,
54 } busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE;
56 static uint32_t busvoodoo_uart_generic_baudrate = 115200;
62 static uint32_t busvoodoo_uart_generic_stopbits = USART_STOPBITS_1;
66 static bool busvoodoo_uart_generic_drive = true;
68 static bool busvoodoo_uart_generic_pullup = false;
69 
74 #define BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE false
75 
77 {
78  busvoodoo_uart_generic_specific = NULL; // reset specific information
79  if (NULL==conf) {
80  return false;
81  }
82  if (!conf->usart || !conf->usart_rcc || !conf->usart_rst) {
83  return false;
84  }
85  if (!conf->tx_rcc || !conf->rx_rcc) {
86  return false;
87  }
88  if (conf->hwflowctl && (!conf->rts_rcc || !conf->cts_rcc)) {
89  return false;
90  }
91  if (conf->timer && (!conf->timer_rcc || !conf->timer_port_rcc || !(NVIC_TIM2_IRQ==conf->timer_nvic_irq || NVIC_TIM3_IRQ==conf->timer_nvic_irq || NVIC_TIM4_IRQ==conf->timer_nvic_irq || NVIC_TIM5_IRQ==conf->timer_nvic_irq))) {
92  return false;
93  }
95  return true;
96 }
97 
98 bool busvoodoo_uart_generic_setup(char** prefix, const char* line)
99 {
100  if (NULL==busvoodoo_uart_generic_specific) { // there is nothing to configure
101  return true;
102  }
103  bool complete = false; // is the setup complete
104  if (NULL==line) { // first call
105  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // re-start configuration
106  }
108  case BUSVOODOO_UART_SETTING_NONE:
110  *prefix = busvoodoo_global_string; // ask for baud rate
111  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_BAUDRATE;
112  break;
113  case BUSVOODOO_UART_SETTING_BAUDRATE:
114  if (NULL==line || 0==strlen(line)) { // use default setting
115  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting
116  } else { // setting provided
117  uint32_t baudrate = atoi(line); // parse setting
118  if (baudrate>0 && baudrate<=2000000) { // check setting
119  busvoodoo_uart_generic_baudrate = baudrate; // remember setting
120  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting
121  }
122  }
123  if (BUSVOODOO_UART_SETTING_DATABITS==busvoodoo_uart_generic_setting) { // if next setting
124  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "data bits (5-8) [%u]", busvoodoo_uart_generic_databits); // prepare next setting
125  *prefix = busvoodoo_global_string; // display next setting
126  }
127  break;
128  case BUSVOODOO_UART_SETTING_DATABITS:
129  if (NULL==line || 0==strlen(line)) { // use default setting
130  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting
131  } else if (1==strlen(line)) { // setting provided
132  uint8_t databits = atoi(line); // parse setting
133  if (databits>=5 && databits<=8) { // check setting
134  busvoodoo_uart_generic_databits = databits; // remember setting
135  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting
136  }
137  }
138  if (BUSVOODOO_UART_SETTING_PARITY==busvoodoo_uart_generic_setting) { // if next setting
139  printf("1) none\n");
140  printf("2) even\n");
141  printf("3) odd\n");
142  printf("4) mark\n");
143  printf("5) space\n");
144  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "parity (1,2,3,4,5) [%u]", busvoodoo_uart_generic_parity+1); // prepare next setting
145  *prefix = busvoodoo_global_string; // display next setting
146  }
147  break;
148  case BUSVOODOO_UART_SETTING_PARITY:
149  if (NULL==line || 0==strlen(line)) { // use default setting
150  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
151  } else if (1==strlen(line)) { // setting provided
152  uint8_t parity = atoi(line); // parse setting
153  if (parity>0 && parity<6) { // check setting
155  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
156  }
157  }
158  if (BUSVOODOO_UART_SETTING_STOPBITS==busvoodoo_uart_generic_setting) { // if next setting
159  printf("1) 0.5\n");
160  printf("2) 1\n");
161  printf("3) 1.5\n");
162  printf("4) 2\n");
163  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "stop bits (1,2,3,4) [%c]", USART_STOPBITS_0_5==busvoodoo_uart_generic_stopbits ? '1' : (USART_STOPBITS_1==busvoodoo_uart_generic_stopbits ? '2' : (USART_STOPBITS_1_5==busvoodoo_uart_generic_stopbits ? '3' : '4'))); // prepare next setting
164  *prefix = busvoodoo_global_string; // display next setting
165  }
166  break;
167  case BUSVOODOO_UART_SETTING_STOPBITS:
168  if (NULL==line || 0==strlen(line)) { // use default setting
169  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
170  } else if (1==strlen(line)) { // setting provided
171  if ('1'==line[0]) { // 0.5 stop bits
172  busvoodoo_uart_generic_stopbits = USART_STOPBITS_0_5; // remember setting
173  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
174  } else if ('2'==line[0]) { // 1 stop bits
175  busvoodoo_uart_generic_stopbits = USART_STOPBITS_1; // remember setting
176  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
177  } else if ('3'==line[0]) { // 1.5 stop bits
178  busvoodoo_uart_generic_stopbits = USART_STOPBITS_1_5; // remember setting
179  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
180  } else if ('4'==line[0]) { // 2 stop bits
181  busvoodoo_uart_generic_stopbits = USART_STOPBITS_2; // remember setting
182  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
183  }
184  }
185  if (BUSVOODOO_UART_SETTING_HWFLOWCTL==busvoodoo_uart_generic_setting) { // if next setting
186  if (!busvoodoo_uart_generic_specific->hwflowctl) { // hardware flow control is not supported
187  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
188  goto setting_drive; // actually go to next setting
189  }
190  printf("1) no flow control\n");
191  printf("2) RTS/CTS hardware flow control\n");
192  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "flow control (1,2) [%c]", busvoodoo_uart_generic_hwflowctl ? '2' : '1'); // prepare next setting
193  *prefix = busvoodoo_global_string; // display next setting
194  }
195  break;
196  case BUSVOODOO_UART_SETTING_HWFLOWCTL:
197  if (NULL==line || 0==strlen(line)) { // use default setting
198  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
199  } else if (1==strlen(line)) { // setting provided
200  if ('1'==line[0] || '2'==line[0]) { // setting provided
201  busvoodoo_uart_generic_hwflowctl = ('2'==line[0]); // remember setting
202  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
203  }
204  }
205 setting_drive:
206  if (BUSVOODOO_UART_SETTING_DRIVE==busvoodoo_uart_generic_setting) { // if next setting
208  busvoodoo_uart_generic_drive = true; // only push-pull driving mode is supported
209  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
210  goto setting_done; // actually go to next setting
211  }
212  printf("1) push-pull (3.3V)\n");
213  printf("2) open-drain, with embedded pull-up resistors (2kO)\n");
214  printf("3) open-drain, with external pull-up resistors\n");
215  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "drive mode (1,2,3) [%c]", busvoodoo_uart_generic_drive ? '1' : (busvoodoo_uart_generic_pullup ? '2' : '3')); // show drive mode
216  *prefix = busvoodoo_global_string; // display next setting
217  }
218  break;
219  case BUSVOODOO_UART_SETTING_DRIVE:
220  if (NULL==line || 0==strlen(line)) { // use default setting
221  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
222  } else if (1==strlen(line)) { // setting provided
223  uint8_t drive = atoi(line); // parse setting
224  if (1==drive || 2==drive || 3==drive) { // check setting
225  busvoodoo_uart_generic_drive = (1==drive); // remember setting
226  busvoodoo_uart_generic_pullup = (2==drive); // remember setting
227  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
228  }
229  }
230 setting_done:
231  if (BUSVOODOO_UART_SETTING_DONE==busvoodoo_uart_generic_setting) { // we have all settings, configure UART
232  rcc_periph_clock_enable(RCC_AFIO); // enable clock for USART alternate function
233  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->usart_rcc); // enable clock for USART peripheral
234  rcc_periph_reset_pulse(busvoodoo_uart_generic_specific->usart_rst); // reset USART peripheral
235  usart_set_baudrate(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_baudrate); // set baud rate
236  usart_enhanced_config(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_databits, busvoodoo_uart_generic_parity); // use enhanced USART to configure the USART peripherals, supporting more data-bits and parity configurations
237  usart_set_stopbits(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_stopbits); // set stop bits
239  usart_set_flow_control(busvoodoo_uart_generic_specific->usart, USART_FLOWCONTROL_RTS_CTS); // set RTS/CTS flow control
240  } else {
241  usart_set_flow_control(busvoodoo_uart_generic_specific->usart, USART_FLOWCONTROL_NONE); // set no flow control
242  }
243  usart_set_mode(busvoodoo_uart_generic_specific->usart, USART_MODE_TX_RX); // full-duplex communication
244  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->tx_rcc); // enable clock for USART GPIO peripheral
245  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->rx_rcc); // enable clock for USART GPIO peripheral
247  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->rts_rcc); // enable clock for USART GPIO peripheral
248  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->cts_rcc); // enable clock for USART GPIO peripheral
249  }
250  if (busvoodoo_uart_generic_drive) { // use push-pull drive mode
251  gpio_set_mode(busvoodoo_uart_generic_specific->tx_port, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, busvoodoo_uart_generic_specific->tx_pin); // setup GPIO pin USART transmit
252  gpio_set(busvoodoo_uart_generic_specific->rx_port, busvoodoo_uart_generic_specific->rx_pin); // pull up to avoid noise when not connected
253  gpio_set_mode(busvoodoo_uart_generic_specific->rx_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, busvoodoo_uart_generic_specific->rx_pin); // setup GPIO pin USART receive
254  if (busvoodoo_uart_generic_specific->hwflowctl && busvoodoo_uart_generic_hwflowctl) { // use open drain drive mode
255  gpio_set_mode(busvoodoo_uart_generic_specific->rts_port, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, busvoodoo_uart_generic_specific->rts_pin); // setup GPIO pin USART transmit
256  gpio_set(busvoodoo_uart_generic_specific->cts_port, busvoodoo_uart_generic_specific->cts_pin); // pull up to block transmission unless requested
257  gpio_set_mode(busvoodoo_uart_generic_specific->cts_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, busvoodoo_uart_generic_specific->cts_pin); // setup GPIO pin USART receive
258  }
259  } else {
260  gpio_set_mode(busvoodoo_uart_generic_specific->tx_port, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, busvoodoo_uart_generic_specific->tx_pin); // setup GPIO pin USART transmit
261  gpio_set_mode(busvoodoo_uart_generic_specific->rx_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->rx_pin); // setup GPIO pin USART receive
263  gpio_set_mode(busvoodoo_uart_generic_specific->rts_port, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, busvoodoo_uart_generic_specific->rts_pin); // setup GPIO pin USART transmit
264  gpio_set_mode(busvoodoo_uart_generic_specific->cts_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->cts_pin); // setup GPIO pin USART receive
265  }
266  }
267  if (!busvoodoo_uart_generic_drive && busvoodoo_uart_generic_pullup) { // enable embedded pull-ups if used
268  busvoodoo_embedded_pullup(true); // set embedded pull-ups
269  printf("use LV to set pull-up voltage\n");
270  }
271  usart_enable(busvoodoo_uart_generic_specific->usart); // enable USART
272  // setup timer to measure RX edge timing for baud rate guessing
274  }
275  busvoodoo_led_blue_off(); // disable blue LED because there is no activity
276  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
277  complete = true; // configuration is complete
278  }
279  break;
280  default: // unknown case
281  busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
282  break;
283  }
284  return complete;
285 }
286 
288 {
290  return;
291  }
292  usart_disable(busvoodoo_uart_generic_specific->usart); // disable USART
293  rcc_periph_clock_disable(busvoodoo_uart_generic_specific->usart_rcc); // disable domain clock
294  gpio_set_mode(busvoodoo_uart_generic_specific->tx_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->tx_pin); // set pin back to floating input
295  gpio_set_mode(busvoodoo_uart_generic_specific->rx_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->rx_pin); // set pin back to floating input
297  gpio_set_mode(busvoodoo_uart_generic_specific->rts_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->rts_pin); // set pin back to floating input
298  gpio_set_mode(busvoodoo_uart_generic_specific->cts_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->cts_pin); // set pin back to floating input
299  }
301  busvoodoo_embedded_pullup(false); // disable embedded pull-ups
302  }
303  busvoodoo_uart_generic_specific = NULL; // remove specific information
304 }
305 
309 static void busvoodoo_uart_generic_write(uint8_t value)
310 {
312  return;
313  }
316  }
317  while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty (or user to interrupt)
318  if ((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TXE) { // we can send data
319  // send data
320  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show transmission
322  // display data send
323  printf("write: '%c'/0x%02x\n", value, value);
324  }
325  while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TC) && !user_input_available)); // wait for transfer to be complete
326  if (user_input_available) { // user interrupted flow
327  user_input_get(); // discard user input
328  }
331  }
332 }
333 
337 {
339  return;
340  }
343  }
344  printf("read: ");
345  while (!(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE) && !user_input_available); // wait for incoming data to be available (or user input to exit)
346  if ((USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE)) { // verify if data has been received
347  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
348  // get the errors
349  bool error_noise = (0!=(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_NE)); // read noise error flag
350  bool error_framing = (0!=(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_FE)); // read frame error flag
351  uint8_t input = usart_enhanced_recv(busvoodoo_uart_generic_specific->usart); // read received character (also clears the error flags)
352  // display data
353  printf("'%c'/0x%02x", input, input);
354  // display errors
355  printf("(");
356  if (error_noise) {
357  printf("noise");
358  } else if (error_framing) {
359  printf("framing");
361  printf("parity");
362  } else {
363  printf("no");
364  }
365  printf(" error)");
366  }
367  printf("\n");
368  if (user_input_available) { // user interrupted flow
369  user_input_get(); // discard user input
370  }
373  }
374 }
375 
382 static bool busvoodoo_uart_generic_action(const char* action, uint32_t repetition, bool perform)
383 {
384  uint32_t length = strlen(action); // remember length since it will be used a number of times
385  if (NULL==action || 0==length) { // there is nothing to do
386  return true;
387  }
388 
389  if (1==length && 'r'==action[0]) { // read data
390  if (!perform) {
391  return true;
392  }
393  for (uint32_t i=0; i<repetition; i++) {
394  busvoodoo_uart_generic_read(); // read from UART
395  }
396  } else if (1==length && 'u'==action[0]) { // sleep us
397  if (!perform) {
398  return true;
399  }
400  printf("wait for %u us\n", repetition);
401  sleep_us(repetition); // sleep
402  } else if (1==length && 'm'==action[0]) { // sleep ms
403  if (!perform) {
404  return true;
405  }
406  printf("wait for %u ms\n", repetition);
407  sleep_ms(repetition); // sleep
408  } else if ('0'==action[0]) { // send digit
409  if (1==length) { // just send 0
410  if (!perform) {
411  return true;
412  }
413  for (uint32_t i=0; i<repetition; i++) {
414  busvoodoo_uart_generic_write(0); // write to UART
415  }
416  } else if ('x'==action[1] || 'b'==action[1]) { // send hex/binary
417  return busvoodoo_uart_generic_action(action+1, repetition, perform); // just retry without leading 0
418  } else if (action[1]>='0' && action[1]<='9') { // send decimal
419  return busvoodoo_uart_generic_action(action+1, repetition, perform); // just retry without leading 0
420  } else { // malformed action
421  return false;
422  }
423  } else if ('x'==action[0] && length>1) { // send hexadecimal value
424  for (uint32_t i=1; i<length; i++) { // check string
425  if (!((action[i]>='0' && action[i]<='9') || (action[i]>='a' && action[i]<='f') || (action[i]>='A' && action[i]<='F'))) { // check for hexadecimal character
426  return false; // not an hexadecimal string
427  }
428  }
429  if (!perform) {
430  return true;
431  }
432  uint32_t value = strtol(&action[1], NULL, 16); // get hex value
433  for (uint32_t i=0; i<repetition; i++) {
434  busvoodoo_uart_generic_write(value); // write to SPI
435  }
436  } else if ('b'==action[0] && length>1) { // send binary value
437  for (uint32_t i=1; i<length; i++) { // check string
438  if (action[i]<'0' || action[i]>'1') { // check for binary character
439  return false; // not a binary string
440  }
441  }
442  if (!perform) {
443  return true;
444  }
445  uint32_t value = strtol(&action[1], NULL, 2); // get binary value
446  for (uint32_t i=0; i<repetition; i++) {
447  busvoodoo_uart_generic_write(value); // write to SPI
448  }
449  } else if (action[0]>='1' && action[0]<='9') { // send decimal value
450  for (uint32_t i=1; i<length; i++) { // check string
451  if (action[i]<'0' || action[i]>'9') { // check for decimal character
452  return false; // not a decimal string
453  }
454  }
455  if (!perform) {
456  return true;
457  }
458  uint32_t value = strtol(&action[0], NULL, 10); // get decimal value
459  for (uint32_t i=0; i<repetition; i++) {
460  busvoodoo_uart_generic_write(value); // write to SPI
461  }
462  } else if (length>=2 && ('"'==action[0] || '\''==action[0]) && (action[length-1]==action[0])) { // send ASCII character
463  if (!perform) {
464  return true;
465  }
466  for (uint32_t r=0; r<repetition; r++) {
467  for (uint32_t i=1; i<length-1; i++) { // go through string
468  busvoodoo_uart_generic_write(action[i]); // write to SPI
469  }
470  }
471  } else { // malformed action
472  return false;
473  }
474  return true; // all went well
475 }
476 
477 // command handlers
478 
482 static void busvoodoo_uart_generic_command_actions(void* argument)
483 {
484  if (NULL==argument || 0==strlen(argument)) {
485  printf("available actions (separated by space or ,):\n");
486  printf("0\twrite decimal value\n");
487  printf("0x0\twrite hexadecimal value\n");
488  printf("0b0\twrite binary value\n");
489  printf("\"a\"/'a'\twrite ASCII characters\n");
490  printf("r\tread value\n");
491  printf("u/m\twait 1 us/ms\n");
492  printf(":n\trepeat action n times\n");
493  return;
494  }
495 
496  // copy argument since it will be modified
497  char* copy = calloc(strlen(argument)+1, sizeof(char));
498  if (!copy) {
499  while (true);
500  }
501  strncpy(copy, argument, strlen(argument)+1);
502  // verify and perform actions
503  if (!busvoodoo_global_actions(copy, false, &busvoodoo_uart_generic_action)) { // verify actions
504  printf("malformed action(s)\n");
505  } else { // action are ok
506  printf("press any key to exit\n");
507  busvoodoo_global_actions(argument, true, &busvoodoo_uart_generic_action); // perform action
508  if (user_input_available) { // user interrupted flow
509  user_input_get(); // discard user input
510  }
511  }
512  free(copy); // release memory
513 }
514 
518 static void busvoodoo_uart_generic_command_transmit(void* argument)
519 {
521  return;
522  }
525  }
526  if (NULL==argument || 0==strlen(argument)) { // nothing to transmit
527  argument = "\r\n"; // transmit CR+LF
528  }
529  printf("press any key to exit\n");
530  for (uint16_t i=0; ((char*)(argument))[i] && !user_input_available; i++) {
531  while ((0==(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
532  if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) { // we can send a character
533  printf("%c", ((char*)(argument))[i]); // echo character to transmit
534  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show transmission
535  usart_enhanced_send(busvoodoo_uart_generic_specific->usart, ((char*)(argument))[i]); // transmit character
536  }
537  }
538  while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TC) && !user_input_available)); // wait for transfer to be complete
539  if (user_input_available) { // user interrupted flow
540  user_input_get(); // discard user input
541  }
542  if (strcmp(argument, "\r\n")) {
543  printf("\n");
544  }
547  }
548 }
549 
553 static void busvoodoo_uart_generic_command_receive(void* argument)
554 {
555  bool display_hex = false; // display in hex
556  bool display_bin = false; // display in bin
557  if (NULL!=argument && strlen(argument)>0) {
558  if (0==strcmp(argument, "h") || 0==strcmp(argument, "hex")) { // user wants hexadecimal display
559  display_hex = true; // remember to display in hexadecimal
560  } else if (0==strcmp(argument, "b") || 0==strcmp(argument, "bin")) { // user wants binary display
561  display_bin = true; // remember to display in binary
562  } else {
563  printf("malformed argument\n");
564  return;
565  }
566  }
568  return;
569  }
572  }
573  printf("press any key to exit\n");
574  while (!user_input_available) { // check for user input to exit
575  if ((USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE)) { // verify if data has been received
576  uint8_t input = usart_enhanced_recv(busvoodoo_uart_generic_specific->usart); // receive character
577  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
578  if (display_hex) { // display data in hex
579  printf("%02x ", input);
580  } else if (display_bin) { // display data in binary
581  printf("%08b ", input);
582  } else { // display in ASCII
583  printf("%c", input); // print received character
584  }
585  }
586  }
587  user_input_get(); // discard user input
588  printf("\n"); // get to next line
591  }
592 }
593 
597 static void busvoodoo_uart_generic_command_transceive(void* argument)
598 {
599  (void)argument; // we won't use the argument
601  return;
602  }
605  }
606  printf("press 5 times escape to exit\n");
607  char last_c = 0; // last user character received
608  uint8_t esc_count = 0; // number of times escape has press received
609  while (true) { // check for escape sequence
610  if (user_input_available) { // check if user wants to transmit something
611  char c = user_input_get(); // get user input
612  if (0x1b==c) { // user pressed escape
613  if (0x1b!=last_c) { // this is the first escape press
614  esc_count = 0;
615  }
616  esc_count++; // increment escape count
617  }
618  last_c = c; // remember key press
619  if (esc_count<5) { // check for escape sequence
622  }
625  }
626  while ((0==(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
627  if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) { // we can send a character
628  usart_enhanced_send(busvoodoo_uart_generic_specific->usart, c); // send user character
629  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show transmission
630  }
631  while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TC) && !user_input_available)); // wait for transfer to be complete
634  }
637  }
638  } else { // user wants to exit
639  break; // exit infinite loop
640  }
641  }
642  if ((USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE)) { // verify if data has been received
643  char input = usart_enhanced_recv(busvoodoo_uart_generic_specific->usart); // receive character
644  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
645  printf("%c", input); // print received character
646  }
647  }
648  printf("\n"); // get to next line
651  }
652 }
653 
657 static void busvoodoo_uart_generic_command_error(void* argument)
658 {
659  (void)argument; // argument not used
660  printf("press any key to exit\n");
661  while (!user_input_available) { // wait until user interrupt
662  busvoodoo_uart_generic_read(); // read incoming data (this also checks for errors
663  }
664  user_input_get(); // discard user input
665 }
666 
669  uint8_t databits;
671  bool parity_even;
672  bool parity_odd;
673  bool parity_mark;
676 };
677 
681 static void uart_configuration_reset(struct uart_configuration_t* configuration)
682 {
683  configuration->databits_matching = true;
684  configuration->parity_even = true;
685  configuration->parity_odd = true;
686  configuration->parity_mark = true;
687  configuration->parity_space = true;
688  configuration->parity_possibilities = 4;
689 }
690 
691 volatile bool pulse_flag = false;
692 volatile uint32_t pulse_duration = UINT32_MAX;
695 #if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
696 static void timer_isr(void)
697 #else
698 void TIM_ISR(2)(void)
699 #endif
700 {
702  static uint32_t pulse = UINT32_MAX; // measured pulse duration (MAX is an invalid values)
703  if (timer_get_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF)) { // overflow update event happened
704  timer_clear_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF); // clear flag
705  if (pulse>(UINT32_MAX-0x10000)) { // we can't measure longer pulser (and baud rate < 0.017 bps make no sense)
706  pulse = UINT32_MAX; // invalidate measured pulse
707  } else {
708  pulse += 0x10000; // account for the 16-bit timer limit
709  }
710  }
711  if (timer_get_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccof)) { // capture overflow occurred
713  pulse = UINT32_MAX; // invalidate measured pulse
714  }
716  uint16_t edge = *busvoodoo_uart_generic_specific->timer_ccr; // retrieve captured value (clears flag)
717  if (UINT32_MAX!=pulse) { // only calculate pulse if previous edge is valid
718  pulse = ((pulse&0xffff0000)+edge)-(pulse&0xffff); // calculate pulse duration
719  if (pulse<pulse_duration) { // save new pulse duration if smaller
720  pulse_duration = pulse;
721  pulse_flag = true;
722  }
723  }
724  pulse = edge; // replace with current edge time
725  }
726 }
727 
732 static void busvoodoo_uart_generic_command_detect(void* argument)
733 {
734  (void)argument; // argument not used
736  printf("baud rate detection not possible (no timer available)\n");
737  return;
738  }
739  printf("the more traffic is incoming, the better the detection\n");
740  printf("press any key to exit\n");
741 
742  // setup USART to receive character
743  uint8_t uart_databits = 8; // start with 8 bits since this is the most common case (i.e. no additional parity bit is used)
744  usart_set_baudrate(busvoodoo_uart_generic_specific->usart, 1200); // configure UART to pre-selected baud rate
745  usart_set_databits(busvoodoo_uart_generic_specific->usart, uart_databits); // configure UART to pre-selected data-bits
746  usart_set_stopbits(busvoodoo_uart_generic_specific->usart, USART_STOPBITS_1); // 1 stop-bits also complies to 2 stop-bits
747  usart_set_parity(busvoodoo_uart_generic_specific->usart, USART_PARITY_NONE); // get the raw data since we will do the parity check ourselves
748 
749  // setup timer to generate/measure signal timing
751  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->timer_port_rcc); // enable clock for GPIO peripheral
752  gpio_set_mode(busvoodoo_uart_generic_specific->timer_port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, busvoodoo_uart_generic_specific->timer_pin); // switch pin to input to measure the target transmit baud rate
753  }
754  rcc_periph_clock_enable(busvoodoo_uart_generic_specific->timer_rcc); // enable clock for timer peripheral
755  timer_reset(busvoodoo_uart_generic_specific->timer); // reset timer state
756  timer_disable_counter(busvoodoo_uart_generic_specific->timer); // disable timer to configure it
757  timer_set_mode(busvoodoo_uart_generic_specific->timer, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
758  timer_set_prescaler(busvoodoo_uart_generic_specific->timer, 1-1); // don't use prescale so to get the most precise measurement
759  timer_ic_set_input(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, busvoodoo_uart_generic_specific->timer_ic_in_ti); // configure the input capture ICx to use the right channel TIn
760  timer_ic_set_filter(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, TIM_IC_OFF); // use no filter input to keep precise timing
761  /* ideally we would trigger on any edge, allowing to measure the bit width (on 010 or 101 bit pattern) and calculate the correct baud rate.
762  * sadly the STM32 F1 family timer peripheral does not supporting triggering on both edges at the same time (the F0 family can).
763  * it is possible to simply update the polarity setting to capture the other edge, but this update seems to slow even in a ISR to capture > 1 MHz frequencies (=500 bps).
764  * thus we will start the trigger on the start bit, and wait for the next falling edge.
765  * this way we don't need to change the parity and have twice the time to measure the baud rate.
766  * to calculate the baud rate we just have to divided the frequency by two.
767  * the correct baud rate on the following pattern (HLHL), and since the start bit is the first HL pattern we just have to wait for data starting with 10 (LSb first)
768  */
769  timer_ic_set_polarity(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, TIM_IC_FALLING); // capture on falling end to trigger on the start bit
770  timer_ic_set_prescaler(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic, TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse
771  timer_ic_enable(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic); // enable capture interrupt
772  timer_clear_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccif); // clear input compare flag
773  timer_enable_irq(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_dier_ccie); // enable capture interrupt
774  timer_update_on_overflow(busvoodoo_uart_generic_specific->timer); // only use counter overflow as UEV source (use overflow to measure longer times)
775  timer_clear_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF); // clear overflow flag
776  timer_enable_irq(busvoodoo_uart_generic_specific->timer, TIM_DIER_UIE); // enable update interrupt for timer
777 #if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
778 
779  void (*isr_backup)(void) = interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq]; // backup current timer ISR before replacing it
780  interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq] = &timer_isr; // replace current timer ISR with our to be able to measure the edge timing
781  bool irq_backup = nvic_get_irq_enabled(busvoodoo_uart_generic_specific->timer_nvic_irq); // backup enable IRQ setting
782 #endif
783  nvic_enable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // catch interrupts for this timer
784  pulse_duration = UINT32_MAX; // reset pulse duration
785  timer_enable_counter(busvoodoo_uart_generic_specific->timer); // enable timer
786 
787  // switch on RX
790  }
791 
792  bool reset_state = true; // flag to know if we need to reset the states
793  uint8_t rx_errors; // number of UART receive errors received
794  bool wait_for_idle = false; // flag to wait for an IDLE frame
797  struct uart_configuration_t uart_configurations[] = {
798  { .databits = 5 },
799  { .databits = 6 },
800  { .databits = 8 },
801  { .databits = 7 },
802  };
803  uint8_t uart_configuration_valid = LENGTH(uart_configurations); // current best valid UART configuration index
804  char uart_configuration_parity = '?'; // current best valid UART parity
805  const uint32_t baudrates[] = { 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 576000, 921600 }; // list of standard baud rates, to match with measured frequency
806  uint32_t uart_baudrate = 0; // fastest found baud rate
807  while (!user_input_available) {
808  if (reset_state) { // reset the configuration
809  rx_errors = 0;
810  for (uint8_t i=0; i<LENGTH(uart_configurations); i++) {
811  uart_configuration_reset(&uart_configurations[i]);
812  }
813  usart_recv(busvoodoo_uart_generic_specific->usart); // clear input buffer and allow flag to be set
814  usart_enable(busvoodoo_uart_generic_specific->usart); // ensure UART is enabled
815  reset_state = false;
816  }
817  if (pulse_flag) { // new pulse duration has been measured
818  pulse_flag = false; // clear flag
819  printf("u");
820  uint32_t baudrate = rcc_ahb_frequency/(pulse_duration/2); // calculate baud rate based on measured timing
821  if (baudrate>uart_baudrate+100) { // new higher baud rate detected
822  uart_baudrate = baudrate; // save new baud rate
823  if (uart_baudrate>=1200) { // ensure minimum hardware supported baud rate is respected
824  // search for closest standard baud rate
825  uint32_t standard_baudrate = 0;
826  for (uint8_t i=0; i<LENGTH(baudrates); i++) {
827  if (uart_baudrate>=baudrates[i]*0.9 && uart_baudrate<=baudrates[i]*1.1) { // measured baud rate matches standard baud rate within factor
828  standard_baudrate = baudrates[i]; // remember matching baud rate
829  break; // stop searching for matching baud rate
830  }
831  }
832  if (standard_baudrate) { // matching standard baud rate found
833  uart_baudrate = standard_baudrate; // save matching baud rate
834  }
835  usart_disable(busvoodoo_uart_generic_specific->usart); // disable UART before reconfiguring
836  usart_set_baudrate(busvoodoo_uart_generic_specific->usart, uart_baudrate); // set new baud rate
837  reset_state = true; // reset the states since we set a new baud rate
838  printf("\nnew baud rate: %u bps\n", uart_baudrate); // show measurement frequency
839  } else {
840  printf("\ndetected %u bps baud rate is lower than minimum supported 1200 bps\n", baudrate);
841  }
842  }
843  }
844  if (USART_SR(busvoodoo_uart_generic_specific->usart) & (USART_SR_NE|USART_SR_FE)) { // error on UART received
845  usart_recv(busvoodoo_uart_generic_specific->usart); // clear input buffer and flags
846  rx_errors++; // increment number of errors
847  if (rx_errors>=5) { // the format seems wrong
848  // the threshold must be high enough so the UART peripheral has enough opportunities to synchronize to the start bit (just after and idle frame)
849  // two high probable frame error causes:
850  // - when set to 9 data-bits with high speed 8 data-bits traffic incoming: the next start bit comes right after the stop bit of and 8-bit frame, which is interpreted as faulty 9 data-bits frame stop bit
851  // - when set to 8 data-bits with 9 data-bits (8+1 parity) traffic incoming: the low parity bit is interpreted as faulty stop-bit
852  uart_databits = ((8==uart_databits) ? 9 : 8); // switch between 8 and 9-bit packets
853  usart_disable(busvoodoo_uart_generic_specific->usart); // disable UART before reconfiguring
854  usart_set_databits(busvoodoo_uart_generic_specific->usart, uart_databits); // set new data width
855  reset_state = true;
856  pulse_duration = UINT32_MAX; // also reset the baud rate
857  uart_baudrate = 0; // also reset the baud rate
858  rx_errors = 0; // reset error counter
859  printf("\nrestarting guessing because too detected error\n");
860  } else {
861  wait_for_idle = true; // wait form an IDLE frame so to better sync to the next start bit
862  }
863  }
864  if (wait_for_idle) {
865  /* we have to check the IDLE flag in the main loop instead of just looping over the flag because a hardware fault could prevent it from being set.
866  * from the "STM32F10xxC/D/E silicon limitations" errata, section 2.12.2 "Idle frame is not detected if receiver clock speed is deviated": If the USART receives an idle frame followed by a character, and the clock of the transmitter device is faster than the USART receiver clock, the USART receive signal falls too early when receiving the character start bit, with the result that the idle frame is not detected (IDLE flag is not set).
867  * there this no workaround be it will be fixed when changing the baud rate
868  */
869  if (USART_SR(busvoodoo_uart_generic_specific->usart) | USART_SR_IDLE) {
870  wait_for_idle = false;
871  }
872  if (USART_SR(busvoodoo_uart_generic_specific->usart) | USART_SR_RXNE) {
873  USART_DR(busvoodoo_uart_generic_specific->usart); // empty receive buffer so the IDLE flag can retrigger
874  }
875  }
876  if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE) { // data received
877  uint16_t usart_data = usart_recv(busvoodoo_uart_generic_specific->usart); // save received data (also clears flag)
878  if (0 == uart_baudrate) { // we did not find any valid baud rate yet
879  continue;
880  }
881  uint16_t usart_data_padded = ((8 == uart_databits) ? usart_data | 0xff00 : usart_data | 0xfe00); // pad with 1 (stop bit/idle state) for better word size detection
882  uint16_t usart_data_relevant = usart_data & ~(0xffff << uart_configurations[uart_configuration_valid].databits); // get only the data bits
883  // verify parity and word size
884  for (uint8_t i = 0; i < LENGTH(uart_configurations); i++) {
885  // skip check if we already know the word size is wrong
886  if (!uart_configurations[i].databits_matching) {
887  continue;
888  }
889  // do parity checks
890  if (uart_configurations[i].parity_even) {
891  uart_configurations[i].parity_even &= usart_enhanced_even_parity_lut[usart_data_relevant];
892  }
893  if (uart_configurations[i].parity_odd) {
894  uart_configurations[i].parity_odd &= !usart_enhanced_even_parity_lut[usart_data_relevant];
895  }
896  if (uart_configurations[i].parity_mark) {
897  uart_configurations[i].parity_mark &= (usart_data_padded & (1 << uart_configurations[i].databits));
898  }
899  if (uart_configurations[i].parity_space) {
900  uart_configurations[i].parity_space &= !(usart_data_padded & (1 << uart_configurations[i].databits));
901  }
902  // update parity count
903  uart_configurations[i].parity_possibilities = 0;
904  if (uart_configurations[i].parity_even) {
905  uart_configurations[i].parity_possibilities++;
906  }
907  if (uart_configurations[i].parity_odd) {
908  uart_configurations[i].parity_possibilities++;
909  }
910  if (uart_configurations[i].parity_mark) {
911  uart_configurations[i].parity_possibilities++;
912  }
913  if (uart_configurations[i].parity_space) {
914  uart_configurations[i].parity_possibilities++;
915  }
916  // verify word size
917  uint16_t databits_mask = (0xffff << (uart_configurations[i].databits + ((0 == uart_configurations[i].parity_possibilities) ? 0 : 1))); // mask for bits which should not be cleared
918  if (~usart_data_padded & databits_mask) { // see if bit outside the word size are cleared
919  uart_configurations[i].databits_matching = false;
920  }
921  }
922  bool no_valid_configuration = true;
923  uint8_t new_valid_configuration = LENGTH(uart_configurations);
924  char parity = '?';
925  for (uint8_t i=0; i<LENGTH(uart_configurations); i++) {
926  // skip check the word size is wrong
927  if (!uart_configurations[i].databits_matching) {
928  continue;
929  }
930  no_valid_configuration = false;
931  if (uart_configurations[i].parity_possibilities > 1) { // parity is not yet clear
932  continue;
933  } else if (uart_configurations[i].parity_even) {
934  parity = 'E';
935  } else if (uart_configurations[i].parity_odd) {
936  parity = 'O';
937  } else if (uart_configurations[i].parity_mark) {
938  parity = 'M';
939  } else if (uart_configurations[i].parity_space) {
940  parity = 'S';
941  } else if (0==uart_configurations[i].parity_possibilities) {
942  parity = 'N';
943  }
944  new_valid_configuration = i;
945  break; // stop searching since we found a configuration
946  }
947  if (no_valid_configuration) {
948  reset_state = true; // reset the configurations
949  pulse_duration = UINT32_MAX; // also reset the baud rate
950  uart_baudrate = 0; // also reset the baud rate
951  } else if (new_valid_configuration < LENGTH(uart_configurations) && '?' != parity && (new_valid_configuration != uart_configuration_valid || parity != uart_configuration_parity)) { // we found a new valid configuration
952  uart_configuration_valid = new_valid_configuration;
953  uart_configuration_parity = parity;
954  printf("\nnew UART configuration found: %u %u%c1\n", uart_baudrate, uart_configurations[uart_configuration_valid].databits, uart_configuration_parity);
955  }
956  // print received data if a configuration has been found
957  if (uart_configuration_valid < LENGTH(uart_configurations)) { // valid configuration existing
958  if (uart_configurations[uart_configuration_valid].databits >= 7 && usart_data_relevant < 0x80) { // this is probably valid ASCII data
959  printf("%c", usart_data_relevant);
960  } else {
961  printf("0x%02x ", usart_data_relevant);
962  }
963  } else {
964  printf("0b%09b\n", usart_data_relevant);
965  }
966  }
967  }
968  user_input_get(); // clear input used to interrupt previous loop
969  printf("\n");
970 
971  // switch off RX
974  }
975 
976  // stop timer
977  timer_disable_counter(busvoodoo_uart_generic_specific->timer); // disable timer
978 #if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
979  interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq] = isr_backup; // restore ISR
980  if (!irq_backup) {
981  nvic_disable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // disable IRQ
982  }
983 #else
984  nvic_disable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // disable IRQ
985 #endif
986  timer_reset(busvoodoo_uart_generic_specific->timer); // reset timer
987  rcc_periph_clock_disable(busvoodoo_uart_generic_specific->timer_rcc); // disable clock for timer peripheral
988  // no need to disable pin since it's already a floating input it is not Rx
989 
990  if (uart_configuration_valid < LENGTH(uart_configurations)) {
991  printf("press y to use configuration found: %u %u%c1\n", uart_baudrate, uart_configurations[uart_configuration_valid].databits, uart_configuration_parity);
992  while (!user_input_available); // wait until user input
993  if ('y' == user_input_get()) { // user want to use found configuration
994  busvoodoo_uart_generic_baudrate = uart_baudrate;
995  busvoodoo_uart_generic_databits = uart_configurations[uart_configuration_valid].databits;
996  if (uart_configurations[uart_configuration_valid].parity_even) {
998  } else if (uart_configurations[uart_configuration_valid].parity_odd) {
1000  } else if (uart_configurations[uart_configuration_valid].parity_mark) {
1002  } else if (uart_configurations[uart_configuration_valid].parity_space) {
1004  } else {
1006  }
1007  busvoodoo_uart_generic_stopbits = USART_STOPBITS_1;
1008  }
1009  }
1010 
1011  // reconfigure USART
1012  usart_set_baudrate(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_baudrate); // set baud rate
1013  usart_enhanced_config(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_databits, busvoodoo_uart_generic_parity); // use enhanced USART to configure the USART peripherals, supporting more data-bits and parity configurations
1014  usart_set_stopbits(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_stopbits); // set stop bits
1015 }
1016 
1017 
1019  {
1020  .shortcut = 'a',
1021  .name = "action",
1022  .command_description = "perform protocol actions",
1023  .argument = MENU_ARGUMENT_STRING,
1024  .argument_description = "[actions]",
1025  .command_handler = &busvoodoo_uart_generic_command_actions,
1026  },
1027  {
1028  .shortcut = 'r',
1029  .name = "receive",
1030  .command_description = "show incoming data [in hexadecimal or binary]",
1031  .argument = MENU_ARGUMENT_STRING,
1032  .argument_description = "[hex|bin]",
1033  .command_handler = &busvoodoo_uart_generic_command_receive,
1034  },
1035  {
1036  .shortcut = 't',
1037  .name = "transmit",
1038  .command_description = "transmit ASCII text (empty for CR+LF)",
1039  .argument = MENU_ARGUMENT_STRING,
1040  .argument_description = "[text]",
1041  .command_handler = &busvoodoo_uart_generic_command_transmit,
1042  },
1043  {
1044  .shortcut = 'x',
1045  .name = "transceive",
1046  .command_description = "transmit and receive data",
1047  .argument = MENU_ARGUMENT_NONE,
1048  .argument_description = NULL,
1049  .command_handler = &busvoodoo_uart_generic_command_transceive,
1050  },
1051  {
1052  .shortcut = 'e',
1053  .name = "error",
1054  .command_description = "verify incoming transmission for errors",
1055  .argument = MENU_ARGUMENT_NONE,
1056  .argument_description = NULL,
1057  .command_handler = &busvoodoo_uart_generic_command_error,
1058  },
1059  {
1060  .shortcut = 'd',
1061  .name = "detect",
1062  .command_description = "auto-detect serial configuration (baud rate, data bits, parity)",
1063  .argument = MENU_ARGUMENT_NONE,
1064  .argument_description = NULL,
1065  .command_handler = &busvoodoo_uart_generic_command_detect,
1066  },
1067 };
1068 
static uint32_t busvoodoo_uart_generic_baudrate
UART baud rate (in bps)
uint32_t timer_pin
pin of timer capture channel
static enum busvoodoo_uart_generic_setting_t busvoodoo_uart_generic_setting
current mode setup stage
bool busvoodoo_uart_generic_setup(char **prefix, const char *line)
setup generic UART mode
volatile uint32_t pulse_duration
smallest pulse duration measured
command menu entry
Definition: menu.h:31
bool parity_odd
if the date is still matching the additional odd parity bit
#define BUSVOODOO_LED_PULSE
recommended duration in ms for pulsing LEDs to show activity
void usart_enhanced_send(uint32_t usart, uint8_t data)
send data over the enhanced USART using the configuration
the possible properties of a UART configuration (to be updated with every character) ...
static bool busvoodoo_uart_generic_hwflowctl
UART hardware flow control setting (true = with hardware flow control, false = without hardware flow ...
BusVoodoo global definitions and methods (API)
static bool busvoodoo_uart_generic_action(const char *action, uint32_t repetition, bool perform)
perform UART action
busvoodoo_uart_generic_setting_t
mode setup stage
uint8_t parity_possibilities
the number to still matching parity possibilities (number of parity_* at true)
void(* tx_post)(void)
method to be called after transmitting data
void busvoodoo_led_blue_pulse(uint32_t ms)
pulse blue LED for short duration
enum tim_ic_id timer_ic
timer input capture channel
volatile bool pulse_flag
set when a small pulse time is detected
uint32_t rx_pin
GPIO pin address of receive pin.
uint32_t timer_port
port of timer capture channel
global definitions and methods (API)
uint32_t timer_dier_ccie
timer channel capture interrupt enable
uint32_t timer_port_rcc
port RCC of timer capture channel
uint32_t rts_port
GPIO port address of request to send pin.
volatile uint32_t * timer_ccr
timer channel capture register
void sleep_us(uint32_t duration)
go to sleep for some microseconds
Definition: global.c:102
static void busvoodoo_uart_generic_command_actions(void *argument)
command to perform actions
static const struct busvoodoo_uart_generic_specific_t * busvoodoo_uart_generic_specific
the USART mode specific information
static void busvoodoo_uart_generic_write(uint8_t value)
write to UART
usart_enhanced_parity_t
enhanced USART setting for the additional parity bit
static void busvoodoo_uart_generic_command_error(void *argument)
command to verify incoming transmission for error
static void busvoodoo_uart_generic_command_detect(void *argument)
command to auto-detect incoming UART configuration (baud rate, data bits, parity) ...
void TIM_ISR(2)
timer ISR to measure edge timing
void busvoodoo_led_blue_off(void)
switch off blue LED
void(* rx_pre)(void)
method to be called before receiving data
uint32_t usart
USART peripheral base address.
bool parity_even
if the date is still matching the additional even parity bit
uint32_t rx_rcc
GPIO RCC address of receive pin.
bool multidrive
if multiple drive modes are supported (push-pull, open-drain with internal resistors, open-drain with external resistors), or just push-pull
uint32_t rx_port
GPIO port address of receive pin.
uint32_t timer_rcc
timer RCC address
uint32_t tx_pin
GPIO pin address of transmit pin.
void(* rx_post)(void)
method to be called after receiving data
void busvoodoo_uart_generic_exit(void)
exit genetic UART mode
char shortcut
short command code (0 if not available)
Definition: menu.h:32
vector_table_entry_t interrupt_table[NVIC_IRQ_COUNT]
table of interrupts which can set to user functions
Definition: interrupt.c:21
uint32_t tx_rcc
GPIO RCC address of transmit pin.
UART specific methods that will be called by the generic methods.
uint32_t timer_sr_ccif
timer channel capture interrupt flag
void(* tx_pre)(void)
method to be called before transmitting data
static bool busvoodoo_uart_generic_pullup
if embedded pull-up resistors are used
uint32_t rts_rcc
GPIO RCC address of request to send pin.
static void busvoodoo_uart_generic_command_transmit(void *argument)
command to transmit a string
uint32_t cts_port
GPIO port address of clear to send pin.
static enum usart_enhanced_parity_t busvoodoo_uart_generic_parity
UART parity setting.
char user_input_get(void)
get user input
Definition: global.c:145
char busvoodoo_global_string[64]
shared string buffer, i.e.
#define busvoodoo_uart_generic_commands_nb
number of commands supported by the generic UART mode
BusVoodoo runtime interrupt table.
uint32_t usart_rst
USART RST address.
bool busvoodoo_global_actions(char *actions, bool perform, bool(*action_handler)(const char *action, uint32_t repetition, bool perform))
parse and perform actions
void sleep_ms(uint32_t duration)
go to sleep for some milliseconds
Definition: global.c:117
uint32_t rts_pin
GPIO pin address of request to send pin.
bool busvoodoo_uart_generic_configure(const struct busvoodoo_uart_generic_specific_t *conf)
provide the generic USART with mode specific information
uint8_t databits
data word size in bits
uint32_t usart_rcc
USART RCC address.
bool usart_enhanced_config(uint32_t usart, uint8_t databits, enum usart_enhanced_parity_t parity)
configure enhanced USART
static void busvoodoo_uart_generic_read(void)
read from UART
BusVoodoo generic UART mode (API)
static uint32_t busvoodoo_uart_generic_stopbits
UART stop bits setting.
static void busvoodoo_uart_generic_command_receive(void *argument)
command to receive data
uint32_t timer_sr_ccof
timer channel capture overrun flag
uint32_t cts_rcc
GPIO RCC address of clear to send pin.
bool databits_matching
if the data is still matching the data bits
uint8_t usart_enhanced_recv(uint32_t usart)
receive data over the enhanced USART using the configuration
const struct menu_command_t busvoodoo_uart_generic_commands[busvoodoo_uart_generic_commands_nb]
commands supported by the generic UART mode
float busvoodoo_embedded_pullup(bool on)
enable embedded pull-up resistors
static bool busvoodoo_uart_generic_drive
pin drive mode (true = push-pull, false = open-drain)
bool parity_space
if the date is still matching the additional space parity bit
#define LENGTH(x)
get the length of an array
Definition: global.h:26
bool hwflowctl
if RTC/CTS hardware flow control is supported
static void busvoodoo_uart_generic_command_transceive(void *argument)
command to transmit and receive data
static void uart_configuration_reset(struct uart_configuration_t *configuration)
reset all matching values of UART configuration
bool parity_mark
if the date is still matching the additional mark parity bit
volatile bool user_input_available
flag set when user input is available
Definition: global.c:36
static uint8_t busvoodoo_uart_generic_databits
UART data bits.
uint32_t tx_port
GPIO port address of transmit pin.
bool usart_enhanced_parity_error(uint32_t usart)
get the parity status of the received data
library for enhanced USART communication (API)
uint32_t cts_pin
GPIO pin address of clear to send pin.
enum tim_ic_input timer_ic_in_ti
timer input capture channel TIn
const bool usart_enhanced_even_parity_lut[256]
know if there is an even number of 1&#39;s in a integer