CuVoodoo STM32F1 firmware template
busvoodoo_uart.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/stm32/gpio.h> // general purpose input output library
28 #include <libopencm3/stm32/rcc.h> // real-time control clock library
29 #include <libopencm3/stm32/usart.h> // USART utilities
30 
31 /* own libraries */
32 #include "global.h" // board definitions
33 #include "print.h" // printing utilities
34 #include "menu.h" // menu definitions
35 #include "busvoodoo_global.h" // BusVoodoo definitions
36 #include "busvoodoo_oled.h" // OLED utilities
37 #include "busvoodoo_uart.h" // own definitions
38 
42 #define BUSVOODOO_USART_ID 3
46 static enum busvoodoo_uart_setting_t {
47  BUSVOODOO_UART_SETTING_NONE,
48  BUSVOODOO_UART_SETTING_BAUDRATE,
49  BUSVOODOO_UART_SETTING_DATABITS,
50  BUSVOODOO_UART_SETTING_PARITY,
51  BUSVOODOO_UART_SETTING_STOPBITS,
52  BUSVOODOO_UART_SETTING_HWFLOWCTL,
53  BUSVOODOO_UART_SETTING_DRIVE,
54  BUSVOODOO_UART_SETTING_DONE,
55 } busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE;
57 static uint32_t busvoodoo_uart_baudrate = 115200;
59 static uint8_t busvoodoo_uart_databits = 8;
61 static uint32_t busvoodoo_uart_parity = USART_PARITY_NONE;
63 static uint32_t busvoodoo_uart_stopbits = USART_STOPBITS_1;
65 static bool busvoodoo_uart_hwflowctl = false;
67 static bool busvoodoo_uart_drive = true;
69 static bool busvoodoo_uart_pullup = false;
70 
76 static bool busvoodoo_uart_setup(char** prefix, const char* line)
77 {
78  bool complete = false; // is the setup complete
79  if (NULL==line) { // first call
80  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // re-start configuration
81  }
82  switch (busvoodoo_uart_setting) {
83  case BUSVOODOO_UART_SETTING_NONE:
85  *prefix = busvoodoo_global_string; // ask for baud rate
86  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_BAUDRATE;
87  break;
88  case BUSVOODOO_UART_SETTING_BAUDRATE:
89  if (NULL==line || 0==strlen(line)) { // use default setting
90  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting
91  } else { // setting provided
92  uint32_t baudrate = atoi(line); // parse setting
93  if (baudrate>0 && baudrate<=2000000) { // check setting
94  busvoodoo_uart_baudrate = baudrate; // remember setting
95  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting
96  }
97  }
98  if (BUSVOODOO_UART_SETTING_DATABITS==busvoodoo_uart_setting) { // if next setting
99  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "data bits (8-9) [%u]", busvoodoo_uart_databits); // prepare next setting
100  *prefix = busvoodoo_global_string; // display next setting
101  }
102  break;
103  case BUSVOODOO_UART_SETTING_DATABITS:
104  if (NULL==line || 0==strlen(line)) { // use default setting
105  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting
106  } else { // setting provided
107  uint8_t databits = atoi(line); // parse setting
108  if (8==databits || 9==databits) { // check setting
109  busvoodoo_uart_databits = databits; // remember setting
110  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting
111  }
112  }
113  if (BUSVOODOO_UART_SETTING_PARITY==busvoodoo_uart_setting) { // if next setting
114  printf("1) none\n");
115  printf("2) even\n");
116  printf("3) odd\n");
117  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "parity (1,2,3) [%c]", USART_PARITY_NONE==busvoodoo_uart_parity ? '1' : (USART_PARITY_EVEN==busvoodoo_uart_parity ? '2' : '3')); // prepare next setting
118  *prefix = busvoodoo_global_string; // display next setting
119  }
120  break;
121  case BUSVOODOO_UART_SETTING_PARITY:
122  if (NULL==line || 0==strlen(line)) { // use default setting
123  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
124  } else if (1==strlen(line)) { // setting provided
125  if ('1'==line[0]) { // no parity
126  busvoodoo_uart_parity = USART_PARITY_NONE;
127  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
128  } else if ('2'==line[0]) { // even parity
129  busvoodoo_uart_parity = USART_PARITY_EVEN;
130  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
131  } else if ('3'==line[0]) { // odd parity
132  busvoodoo_uart_parity = USART_PARITY_ODD;
133  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
134  }
135  }
136  if (BUSVOODOO_UART_SETTING_STOPBITS==busvoodoo_uart_setting) { // if next setting
137  printf("1) 0.5\n");
138  printf("2) 1\n");
139  printf("3) 1.5\n");
140  printf("4) 2\n");
141  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "stop bits (1,2,3,4) [%s]", USART_STOPBITS_0_5==busvoodoo_uart_stopbits ? "0.5" : (USART_STOPBITS_1==busvoodoo_uart_stopbits ? "1" : (USART_STOPBITS_1_5==busvoodoo_uart_stopbits ? "1.5" : "2.0"))); // prepare next setting
142  *prefix = busvoodoo_global_string; // display next setting
143  }
144  break;
145  case BUSVOODOO_UART_SETTING_STOPBITS:
146  if (NULL==line || 0==strlen(line)) { // use default setting
147  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
148  } else if (1==strlen(line)) { // setting provided
149  if ('1'==line[0]) { // 0.5 stop bits
150  busvoodoo_uart_stopbits = USART_STOPBITS_0_5; // remember setting
151  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
152  } else if ('2'==line[0]) { // 1 stop bits
153  busvoodoo_uart_stopbits = USART_STOPBITS_1; // remember setting
154  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
155  } else if ('3'==line[0]) { // 1.5 stop bits
156  busvoodoo_uart_stopbits = USART_STOPBITS_1_5; // remember setting
157  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
158  } else if ('4'==line[0]) { // 2 stop bits
159  busvoodoo_uart_stopbits = USART_STOPBITS_2; // remember setting
160  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
161  }
162  }
163  if (BUSVOODOO_UART_SETTING_HWFLOWCTL==busvoodoo_uart_setting) { // if next setting
164  printf("1) no flow control\n");
165  printf("2) RTS/CTS hardware flow control\n");
166  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "flow control (1,2) [%c]", busvoodoo_uart_hwflowctl ? '2' : '1'); // prepare next setting
167  *prefix = busvoodoo_global_string; // display next setting
168  }
169  break;
170  case BUSVOODOO_UART_SETTING_HWFLOWCTL:
171  if (NULL==line || 0==strlen(line)) { // use default setting
172  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
173  } else if (1==strlen(line)) { // setting provided
174  if ('1'==line[0] || '2'==line[0]) { // setting provided
175  busvoodoo_uart_hwflowctl = ('2'==line[0]); // remember setting
176  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
177  }
178  }
179  if (BUSVOODOO_UART_SETTING_DRIVE==busvoodoo_uart_setting) { // if next setting
180  printf("1) push-pull (3.3V)\n");
181  printf("2) open-drain, with embedded pull-up resistors (2kO)\n");
182  printf("3) open-drain, with external pull-up resistors\n");
183  snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "drive mode (1,2,3) [%c]", busvoodoo_uart_drive ? '1' : (busvoodoo_uart_pullup ? '2' : '3')); // show drive mode
184  *prefix = busvoodoo_global_string; // display next setting
185  }
186  break;
187  case BUSVOODOO_UART_SETTING_DRIVE:
188  if (NULL==line || 0==strlen(line)) { // use default setting
189  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
190  } else if (1==strlen(line)) { // setting provided
191  uint8_t drive = atoi(line); // parse setting
192  if (1==drive || 2==drive || 3==drive) { // check setting
193  busvoodoo_uart_drive = (1==drive); // remember setting
194  busvoodoo_uart_pullup = (2==drive); // remember setting
195  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
196  }
197  }
198  if (BUSVOODOO_UART_SETTING_DONE==busvoodoo_uart_setting) { // we have all settings, configure UART
199  rcc_periph_clock_enable(RCC_AFIO); // enable clock for USART alternate function
200  rcc_periph_clock_enable(RCC_USART(BUSVOODOO_USART_ID)); // enable clock for USART peripheral
201  usart_set_baudrate(USART(BUSVOODOO_USART_ID), busvoodoo_uart_baudrate); // set baud rate
202  usart_set_databits(USART(BUSVOODOO_USART_ID), busvoodoo_uart_databits); // set data bits
203  usart_set_parity(USART(BUSVOODOO_USART_ID), busvoodoo_uart_parity); // set parity
204  usart_set_stopbits(USART(BUSVOODOO_USART_ID), busvoodoo_uart_stopbits); // set stop bits
206  usart_set_flow_control(USART(BUSVOODOO_USART_ID), USART_FLOWCONTROL_RTS_CTS); // set RTS/CTS flow control
207  } else {
208  usart_set_flow_control(USART(BUSVOODOO_USART_ID), USART_FLOWCONTROL_NONE); // set no flow control
209  }
210  usart_set_mode(USART(BUSVOODOO_USART_ID), USART_MODE_TX_RX); // full-duplex communication
211  rcc_periph_clock_enable(RCC_USART_PORT(BUSVOODOO_USART_ID)); // enable clock for USART GPIO peripheral
212  if (busvoodoo_uart_drive) { // use push-pull drive mode
213  gpio_set_mode(USART_TX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_TX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit
214  gpio_set(USART_RX_PORT(BUSVOODOO_USART_ID), USART_RX_PIN(BUSVOODOO_USART_ID)); // pull up to avoid noise when not connected
215  gpio_set_mode(USART_RX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_RX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive
216  if (busvoodoo_uart_hwflowctl) { // use open drain drive mode
217  gpio_set_mode(USART_RTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, USART_RTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit
218  gpio_set(USART_CTS_PORT(BUSVOODOO_USART_ID), USART_CTS_PIN(BUSVOODOO_USART_ID)); // pull up to block transmission unless requested
219  gpio_set_mode(USART_CTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_CTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive
220  }
221  } else {
222  gpio_set_mode(USART_TX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_TX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit
223  gpio_set_mode(USART_RX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_RX_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive
225  gpio_set_mode(USART_RTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, USART_RTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART transmit
226  gpio_set_mode(USART_CTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_CTS_PIN(BUSVOODOO_USART_ID)); // setup GPIO pin USART receive
227  }
228  }
229  if (!busvoodoo_uart_drive && busvoodoo_uart_pullup) { // enable embedded pull-ups if used
230  busvoodoo_embedded_pullup(true); // set embedded pull-ups
231  printf("use LV to set pull-up voltage\n");
232  }
233  usart_enable(USART(BUSVOODOO_USART_ID)); // enable USART
234  busvoodoo_led_blue_off(); // disable blue LED because there is no activity
235  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
236  *prefix = "UART"; // display mode
237  busvoodoo_oled_text_left(*prefix); // set mode title on OLED display
238  char* pinout_io[10] = {"GND", "5V", "3V3", "LV", "Rx", "Tx", NULL, NULL, NULL, NULL}; // UART mode pinout
239  if (busvoodoo_uart_hwflowctl) { // hardware flow control is used
240  pinout_io[6] = "RTS"; // update pin name
241  pinout_io[7] = "CTS"; // update pin name
242  }
243  for (uint8_t i=0; i<LENGTH(pinout_io) && i<LENGTH(busvoodoo_global_pinout_io); i++) {
244  busvoodoo_global_pinout_io[i] = pinout_io[i]; // set pin names
245  }
246  if (busvoodoo_full) {
247  const char* pinout_rscan[5] = {"HV", NULL, NULL, NULL, NULL}; // HiZ mode RS/CAN pinout
248  for (uint8_t i=0; i<LENGTH(pinout_rscan) && i<LENGTH(busvoodoo_global_pinout_rscan); i++) {
249  busvoodoo_global_pinout_rscan[i] = pinout_rscan[i]; // set pin names
250  }
251  }
252  busvoodoo_oled_text_pinout((const char**)pinout_io, true); // set pinout on display
253  busvoodoo_oled_update(); // update display to show text and pinout
254  complete = true; // configuration is complete
255  }
256  break;
257  default: // unknown case
258  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
259  break;
260  }
261  return complete;
262 }
263 
266 static void busvoodoo_uart_exit(void)
267 {
268  usart_disable(USART(BUSVOODOO_USART_ID)); // disable USART
269  rcc_periph_clock_disable(RCC_USART(BUSVOODOO_USART_ID)); // disable domain clock
270  gpio_set_mode(USART_TX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_TX_PIN(BUSVOODOO_USART_ID)); // set pin back to floating input
271  gpio_set_mode(USART_RX_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_RX_PIN(BUSVOODOO_USART_ID)); // set pin back to floating input
272  gpio_set_mode(USART_RTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_RTS_PIN(BUSVOODOO_USART_ID)); // set pin back to floating input
273  gpio_set_mode(USART_CTS_PORT(BUSVOODOO_USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USART_CTS_PIN(BUSVOODOO_USART_ID)); // set pin back to floating input
274  busvoodoo_embedded_pullup(false); // disable embedded pull-ups
275 }
276 
280 static void busvoodoo_uart_write(uint16_t value)
281 {
282  while ((0==(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty (or user to interrupt)
283  if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character
284  // remove unused bits
285  if (USART_PARITY_NONE==busvoodoo_uart_parity) { // no parity bit in frame
286  if (8==busvoodoo_uart_databits) { // 8-bit frame
287  value &= 0xff;
288  } else { // 9-bit frame
289  value &= 0x1ff;
290  }
291  } else { // MSb is parity bit
292  if (8==busvoodoo_uart_databits) { // 8-bit frame
293  value &= 0x7f;
294  } else { // 9-bit frame
295  value &= 0xff;
296  }
297  }
298  // send data
299  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show transmission
300  usart_send(USART(BUSVOODOO_USART_ID), value); // transmit character
301  // display data send
302  printf("write: '%c'/0x", value);
303  if ((USART_PARITY_NONE==busvoodoo_uart_parity) && 9==busvoodoo_uart_databits) { // case where the final data is 9 bits long
304  printf("%03x\n", value);
305  } else {
306  printf("%02x\n", value);
307  }
308  }
309 }
310 
313 static void busvoodoo_uart_read(void)
314 {
315  printf("read: ");
316  while (!(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE) && !user_input_available); // wait for incoming data to be available (or user input to exit)
317  if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received
318  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
319  // get the errors
320  bool error_noise = (0!=(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_NE)); // read noise error flag
321  bool error_framing = (0!=(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_FE)); // read frame error flag
322  bool error_parity = (0!=(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_PE)); // read parity error flag
323  uint16_t c = usart_recv(USART(BUSVOODOO_USART_ID)); // read received character (also clears the error flags)
324  // remove unused bits
325  if (USART_PARITY_NONE==busvoodoo_uart_parity) { // no parity bit in frame
326  if (8==busvoodoo_uart_databits) { // 8-bit frame
327  c &= 0xff;
328  } else { // 9-bit frame
329  c &= 0x1ff;
330  }
331  } else { // MSb is parity bit
332  if (8==busvoodoo_uart_databits) { // 8-bit frame
333  c &= 0x7f;
334  } else { // 9-bit frame
335  c &= 0xff;
336  }
337  }
338  // display data
339  printf("'%c'/0x", c);
340  if ((USART_PARITY_NONE==busvoodoo_uart_parity) && 9==busvoodoo_uart_databits) { // case where the final data is 9 bits long
341  printf("%03x ", c);
342  } else {
343  printf("%02x ", c);
344  }
345  // display errors
346  printf("(");
347  if (error_noise) {
348  printf("noise");
349  } else if (error_framing) {
350  printf("framing");
351  } else if (error_parity) {
352  printf("parity");
353  } else {
354  printf("no");
355  }
356  printf(" error)");
357  }
358  printf("\n");
359 }
360 
367 static bool busvoodoo_uart_action(const char* action, uint32_t repetition, bool perform)
368 {
369  uint32_t length = strlen(action); // remember length since it will be used a number of times
370  if (NULL==action || 0==length) { // there is nothing to do
371  return true;
372  }
373 
374  if (1==length && 'r'==action[0]) { // read data
375  if (!perform) {
376  return true;
377  }
378  for (uint32_t i=0; i<repetition; i++) {
379  busvoodoo_uart_read(); // read from UART
380  }
381  } else if (1==length && 'u'==action[0]) { // sleep us
382  if (!perform) {
383  return true;
384  }
385  printf("wait for %u us\n", repetition);
386  sleep_us(repetition); // sleep
387  } else if (1==length && 'm'==action[0]) { // sleep ms
388  if (!perform) {
389  return true;
390  }
391  printf("wait for %u ms\n", repetition);
392  sleep_ms(repetition); // sleep
393  } else if ('0'==action[0]) { // send digit
394  if (1==length) { // just send 0
395  if (!perform) {
396  return true;
397  }
398  for (uint32_t i=0; i<repetition; i++) {
399  busvoodoo_uart_write(0); // write to UART
400  }
401  } else if ('x'==action[1] || 'b'==action[1]) { // send hex/binary
402  return busvoodoo_uart_action(action+1, repetition, perform); // just retry without leading 0
403  } else if (action[1]>='0' && action[1]<='9') { // send decimal
404  return busvoodoo_uart_action(action+1, repetition, perform); // just retry without leading 0
405  } else { // malformed action
406  return false;
407  }
408  } else if ('x'==action[0] && length>1) { // send hexadecimal value
409  for (uint32_t i=1; i<length; i++) { // check string
410  if (!((action[i]>='0' && action[i]<='9') || (action[i]>='a' && action[i]<='f') || (action[i]>='A' && action[i]<='F'))) { // check for hexadecimal character
411  return false; // not an hexadecimal string
412  }
413  }
414  if (!perform) {
415  return true;
416  }
417  uint32_t value = strtol(&action[1], NULL, 16); // get hex value
418  for (uint32_t i=0; i<repetition; i++) {
419  busvoodoo_uart_write(value); // write to SPI
420  }
421  } else if ('b'==action[0] && length>1) { // send binary value
422  for (uint32_t i=1; i<length; i++) { // check string
423  if (action[i]<'0' || action[i]>'1') { // check for binary character
424  return false; // not a binary string
425  }
426  }
427  if (!perform) {
428  return true;
429  }
430  uint32_t value = strtol(&action[1], NULL, 2); // get binary value
431  for (uint32_t i=0; i<repetition; i++) {
432  busvoodoo_uart_write(value); // write to SPI
433  }
434  } else if (action[0]>='1' && action[0]<='9') { // send decimal value
435  for (uint32_t i=1; i<length; i++) { // check string
436  if (action[i]<'0' || action[i]>'9') { // check for decimal character
437  return false; // not a decimal string
438  }
439  }
440  if (!perform) {
441  return true;
442  }
443  uint32_t value = strtol(&action[0], NULL, 10); // get decimal value
444  for (uint32_t i=0; i<repetition; i++) {
445  busvoodoo_uart_write(value); // write to SPI
446  }
447  } else if (length>=2 && ('"'==action[0] || '\''==action[0]) && (action[length-1]==action[0])) { // send ASCII character
448  if (!perform) {
449  return true;
450  }
451  for (uint32_t r=0; r<repetition; r++) {
452  for (uint32_t i=1; i<length-1; i++) { // go through string
453  busvoodoo_uart_write(action[i]); // write to SPI
454  }
455  }
456  } else { // malformed action
457  return false;
458  }
459  return true; // all went well
460 }
461 
462 
463 // command handlers
464 
468 static void busvoodoo_uart_command_actions(void* argument)
469 {
470  if (NULL==argument || 0==strlen(argument)) {
471  printf("available actions (separated by space or ,):\n");
472  printf("0\twrite decimal value\n");
473  printf("0x0\twrite hexadecimal value\n");
474  printf("0b0\twrite binary value\n");
475  printf("\"a\"/'a'\twrite ASCII characters\n");
476  printf("r\tread value\n");
477  printf("u/m\twait 1 us/ms\n");
478  printf(":n\trepeat action n times\n");
479  return;
480  }
481 
482  // copy argument since it will be modified
483  char* copy = calloc(strlen(argument)+1, sizeof(char));
484  if (!copy) {
485  while (true);
486  }
487  strncpy(copy, argument, strlen(argument)+1);
488  // verify and perform actions
489  if (!busvoodoo_global_actions(copy, false, &busvoodoo_uart_action)) { // verify actions
490  printf("malformed action(s)\n");
491  } else { // action are ok
492  printf("press any key to exit\n");
493  busvoodoo_global_actions(argument, true, &busvoodoo_uart_action); // perform action
494  if (user_input_available) { // user interrupted flow
495  user_input_get(); // discard user input
496  }
497  }
498  free(copy); // release memory
499 }
500 
504 static void busvoodoo_uart_command_transmit(void* argument)
505 {
506  if (NULL==argument || 0==strlen(argument)) { // nothing to transmit
507  argument = "\r\n"; // transmit CR+LF
508  }
509  printf("press any key to exit\n");
510  for (uint16_t i=0; ((char*)(argument))[i] && !user_input_available; i++) {
511  while ((0==(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
512  if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character
513  printf("%c", ((char*)(argument))[i]); // echo character to transmit
514  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show transmission
515  usart_send(USART(BUSVOODOO_USART_ID), ((char*)(argument))[i]); // transmit character
516  }
517  }
518  if (user_input_available) { // user interrupted flow
519  user_input_get(); // discard user input
520  }
521  if (strcmp(argument, "\r\n")) {
522  printf("\n");
523  }
524 }
525 
529 static void busvoodoo_uart_command_receive(void* argument)
530 {
531  bool display_hex = false; // display in hex
532  bool display_bin = false; // display in bin
533  if (NULL!=argument && strlen(argument)>0) {
534  if (0==strcmp(argument, "h") || 0==strcmp(argument, "hex")) { // user wants hexadecimal display
535  display_hex = true; // remember to display in hexadecimal
536  } else if (0==strcmp(argument, "b") || 0==strcmp(argument, "bin")) { // user wants binary display
537  display_bin = true; // remember to display in binary
538  } else {
539  printf("malformed argument\n");
540  return;
541  }
542  }
543  printf("press any key to exit\n");
544  while (!user_input_available) { // check for user input to exit
545  if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received
546  uint16_t c = usart_recv(USART(BUSVOODOO_USART_ID)); // receive character
547  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
548  // remove unused bits (ignore parity bit)
549  if (USART_PARITY_NONE==busvoodoo_uart_parity) { // no parity bit in frame
550  if (8==busvoodoo_uart_databits) { // 8-bit frame
551  c &= 0xff;
552  } else { // 9-bit frame
553  c &= 0x1ff;
554  }
555  } else { // MSb is parity bit
556  if (8==busvoodoo_uart_databits) { // 8-bit frame
557  c &= 0x7f;
558  } else { // 9-bit frame
559  c &= 0xff;
560  }
561  }
562  if (display_hex) { // display data in hex
563  if ((USART_PARITY_NONE==busvoodoo_uart_parity) && 9==busvoodoo_uart_databits) { // case where the final data is 9 bits long
564  printf("%03x ", c);
565  } else {
566  printf("%02x ", c);
567  }
568  } else if (display_bin) { // display data in binary
569  if (USART_PARITY_NONE==busvoodoo_uart_parity) {
570  if (8==busvoodoo_uart_databits) { // 8-bit frame
571  printf("%08b ", c);
572  } else { // 9-bit frame
573  printf("%09b ", c);
574  }
575  } else { // one bit is a parity bit
576  if (8==busvoodoo_uart_databits) { // 8-bit frame
577  printf("%07b ", c);
578  } else { // 9-bit frame
579  printf("%08b ", c);
580  }
581  }
582  } else { // display in ASCII
583  printf("%c", c); // print received character
584  }
585  }
586  }
587  user_input_get(); // discard user input
588  printf("\n"); // get to next line
589 }
590 
594 static void busvoodoo_uart_command_transceive(void* argument)
595 {
596  (void)argument; // we won't use the argument
597  printf("press 5 times escape to exit\n");
598  char last_c = 0; // last user character received
599  uint8_t esc_count = 0; // number of times escape has press received
600  while (true) { // check for escape sequence
601  if (user_input_available) { // check if user wants to transmit something
602  char c = user_input_get(); // get user input
603  if (0x1b==c) { // user pressed escape
604  if (0x1b!=last_c) { // this is the first escape press
605  esc_count = 0;
606  }
607  esc_count++; // increment escape count
608  }
609  last_c = c; // remember key press
610  if (esc_count<5) { // check for escape sequence
611  while ((0==(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
612  if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character
613  usart_send_blocking(USART(BUSVOODOO_USART_ID), c); // send user character
614  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show transmission
615  }
616  } else { // user wants to exit
617  break; // exit infinite loop
618  }
619  }
620  if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received
621  char c = usart_recv(USART(BUSVOODOO_USART_ID)); // receive character
622  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
623  printf("%c", c); // print received character
624  }
625  }
626  printf("\n"); // get to next line
627 }
628 
632 static void busvoodoo_uart_command_error(void* argument)
633 {
634  (void)argument; // argument not used
635  printf("press any key to exit\n");
636  while (!user_input_available) { // wait until user interrupt
637  busvoodoo_uart_read(); // read incoming data (this also checks for errors
638  }
639  user_input_get(); // discard user input
640 }
641 
643 static const struct menu_command_t busvoodoo_uart_commands[] = {
644  {
645  .shortcut = 'a',
646  .name = "action",
647  .command_description = "perform protocol actions",
648  .argument = MENU_ARGUMENT_STRING,
649  .argument_description = "[actions]",
650  .command_handler = &busvoodoo_uart_command_actions,
651  },
652  {
653  .shortcut = 'r',
654  .name = "receive",
655  .command_description = "show incoming data [in hexadecimal or binary]",
656  .argument = MENU_ARGUMENT_STRING,
657  .argument_description = "[hex|bin]",
658  .command_handler = &busvoodoo_uart_command_receive,
659  },
660  {
661  .shortcut = 't',
662  .name = "transmit",
663  .command_description = "transmit ASCII text (empty for CR+LF)",
664  .argument = MENU_ARGUMENT_STRING,
665  .argument_description = "[text]",
666  .command_handler = &busvoodoo_uart_command_transmit,
667  },
668  {
669  .shortcut = 'x',
670  .name = "transceive",
671  .command_description = "transmit and receive data",
672  .argument = MENU_ARGUMENT_NONE,
673  .argument_description = NULL,
674  .command_handler = &busvoodoo_uart_command_transceive,
675  },
676  {
677  .shortcut = 'e',
678  .name = "error",
679  .command_description = "verify incoming transmission for errors",
680  .argument = MENU_ARGUMENT_NONE,
681  .argument_description = NULL,
682  .command_handler = &busvoodoo_uart_command_error,
683  },
684 };
685 
687  .name = "uart",
688  .description = "Universal Asynchronous Receiver-Transmitter",
689  .full_only = false,
690  .setup = &busvoodoo_uart_setup,
691  .commands = busvoodoo_uart_commands,
692  .commands_nb = LENGTH(busvoodoo_uart_commands),
693  .exit = &busvoodoo_uart_exit,
694 };
#define USART_TX_PORT(x)
get port for USART transmit pin based on USART identifier
Definition: global.h:190
#define RCC_USART(x)
get RCC for USART based on USART identifier
Definition: global.h:184
command menu entry
Definition: menu.h:31
#define BUSVOODOO_LED_PULSE
recommended duration in ms for pulsing LEDs to show activity
#define BUSVOODOO_USART_ID
USART peripheral.
BusVoodoo global definitions and methods (API)
void busvoodoo_oled_text_pinout(const char *pins[10], bool io_connector)
draw pin names on bottom (blue) part in display buffer
static bool busvoodoo_uart_pullup
if embedded pull-up resistors are used
const char * name
name of the mode (i.e.
void busvoodoo_led_blue_pulse(uint32_t ms)
pulse blue LED for short duration
static void busvoodoo_uart_command_actions(void *argument)
command to perform actions
#define RCC_USART_PORT(x)
get RCC for USART port based on USART identifier
Definition: global.h:206
void busvoodoo_oled_update(void)
update OLED display RAM with current display buffer
static void busvoodoo_uart_command_transmit(void *argument)
command to transmit a string
const char * busvoodoo_global_pinout_rscan[5]
RS/CAN connector pinout.
global definitions and methods (API)
void sleep_us(uint32_t duration)
go to sleep for some microseconds
Definition: global.c:102
bool busvoodoo_full
is the BusVoodoo board fully populated (with HV voltage regulator, RS-232, RS-485, CAN transceiver on the back side)
static bool busvoodoo_uart_setup(char **prefix, const char *line)
setup UART mode
void busvoodoo_led_blue_off(void)
switch off blue LED
#define USART_RX_PIN(x)
get pin for USART receive pin based on USART identifier
Definition: global.h:200
#define USART_TX_PIN(x)
get pin for USART transmit pin based on USART identifier
Definition: global.h:198
#define USART_CTS_PORT(x)
get port for USART CTS pin based on USART identifier
Definition: global.h:196
const struct busvoodoo_mode_t busvoodoo_uart_mode
UART mode interface definition.
#define USART(x)
get USART based on USART identifier
Definition: global.h:182
static const struct menu_command_t busvoodoo_uart_commands[]
UART menu commands.
char shortcut
short command code (0 if not available)
Definition: menu.h:32
static uint32_t busvoodoo_uart_parity
UART parity setting.
static enum busvoodoo_uart_setting_t busvoodoo_uart_setting
current mode setup stage
static bool busvoodoo_uart_hwflowctl
UART hardware flow control setting (true = with hardware flow control, false = without hardware flow ...
static void busvoodoo_uart_command_transceive(void *argument)
command to transmit and receive data
static bool busvoodoo_uart_action(const char *action, uint32_t repetition, bool perform)
perform UART action
const char * busvoodoo_global_pinout_io[10]
I/O connector pinout.
BusVoodoo UART mode (API)
char user_input_get(void)
get user input
Definition: global.c:145
char busvoodoo_global_string[64]
shared string buffer, i.e.
static uint32_t busvoodoo_uart_stopbits
UART stop bits setting.
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
#define USART_RX_PORT(x)
get port for USART receive pin based on USART identifier
Definition: global.h:192
#define USART_RTS_PIN(x)
get pin for USART RTS pin based on USART identifier
Definition: global.h:202
static void busvoodoo_uart_write(uint16_t value)
write to UART
#define USART_CTS_PIN(x)
get pin for USART CTS pin based on USART identifier
Definition: global.h:204
static void busvoodoo_uart_command_error(void *argument)
command to verify incoming transmission for error
static bool busvoodoo_uart_drive
pin drive mode (true = push-pull, false = open-drain)
void busvoodoo_oled_text_left(char *text)
draw mode text on top (yellow) left side in display buffer
float busvoodoo_embedded_pullup(bool on)
enable embedded pull-up resistors
#define LENGTH(x)
get the length of an array
Definition: global.h:26
static void busvoodoo_uart_command_receive(void *argument)
command to receive data
static uint8_t busvoodoo_uart_databits
UART data bits.
library to show BusVoodoo mode information on SSD1306 OLED display: name, activity, pinout (API)
volatile bool user_input_available
flag set when user input is available
Definition: global.c:36
static void busvoodoo_uart_exit(void)
exit UART mode
BusVoodoo mode interface.
#define USART_RTS_PORT(x)
get port for USART RTS pin based on USART identifier
Definition: global.h:194
static uint32_t busvoodoo_uart_baudrate
UART baud rate (in bps)
static void busvoodoo_uart_read(void)
read from UART