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  led_off(); // disable 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  busvoodoo_oled_text_pinout((const char**)pinout_io, true); // set pinout on display
247  busvoodoo_oled_update(); // update display to show text and pinout
248  complete = true; // configuration is complete
249  }
250  break;
251  default: // unknown case
252  busvoodoo_uart_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
253  break;
254  }
255  return complete;
256 }
257 
260 static void busvoodoo_uart_exit(void)
261 {
262  usart_disable(USART(BUSVOODOO_USART_ID)); // disable USART
263  rcc_periph_clock_disable(RCC_USART(BUSVOODOO_USART_ID)); // disable domain clock
264  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
265  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
266  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
267  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
268  busvoodoo_embedded_pullup(false); // disable embedded pull-ups
269 }
270 
274 static void busvoodoo_uart_write(uint16_t value)
275 {
276  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)
277  if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character
278  // remove unused bits
279  if (USART_PARITY_NONE==busvoodoo_uart_parity) { // no parity bit in frame
280  if (8==busvoodoo_uart_databits) { // 8-bit frame
281  value &= 0xff;
282  } else { // 9-bit frame
283  value &= 0x1ff;
284  }
285  } else { // MSb is parity bit
286  if (8==busvoodoo_uart_databits) { // 8-bit frame
287  value &= 0x7f;
288  } else { // 9-bit frame
289  value &= 0xff;
290  }
291  }
292  // send data
293  busvoodoo_led_red_pulse(BUSVOODOO_LED_PULSE); // pulse red LED to show transmission
294  usart_send(USART(BUSVOODOO_USART_ID), value); // transmit character
295  // display data send
296  printf("write: '%c'/0x", value);
297  if ((USART_PARITY_NONE==busvoodoo_uart_parity) && 9==busvoodoo_uart_databits) { // case where the final data is 9 bits long
298  printf("%03x\n", value);
299  } else {
300  printf("%02x\n", value);
301  }
302  }
303 }
304 
307 static void busvoodoo_uart_read(void)
308 {
309  printf("read: ");
310  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)
311  if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received
312  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
313  // get the errors
314  bool error_noise = (0!=(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_NE)); // read noise error flag
315  bool error_framing = (0!=(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_FE)); // read frame error flag
316  bool error_parity = (0!=(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_PE)); // read parity error flag
317  uint16_t c = usart_recv(USART(BUSVOODOO_USART_ID)); // read received character (also clears the error flags)
318  // remove unused bits
319  if (USART_PARITY_NONE==busvoodoo_uart_parity) { // no parity bit in frame
320  if (8==busvoodoo_uart_databits) { // 8-bit frame
321  c &= 0xff;
322  } else { // 9-bit frame
323  c &= 0x1ff;
324  }
325  } else { // MSb is parity bit
326  if (8==busvoodoo_uart_databits) { // 8-bit frame
327  c &= 0x7f;
328  } else { // 9-bit frame
329  c &= 0xff;
330  }
331  }
332  // display data
333  printf("'%c'/0x", c);
334  if ((USART_PARITY_NONE==busvoodoo_uart_parity) && 9==busvoodoo_uart_databits) { // case where the final data is 9 bits long
335  printf("%03x ", c);
336  } else {
337  printf("%02x ", c);
338  }
339  // display errors
340  printf("(");
341  if (error_noise) {
342  printf("noise");
343  } else if (error_framing) {
344  printf("framing");
345  } else if (error_parity) {
346  printf("parity");
347  } else {
348  printf("no");
349  }
350  printf(" error)");
351  }
352  printf("\n");
353 }
354 
361 static bool busvoodoo_uart_action(const char* action, uint32_t repetition, bool perform)
362 {
363  uint32_t length = strlen(action); // remember length since it will be used a number of times
364  if (NULL==action || 0==length) { // there is nothing to do
365  return true;
366  }
367 
368  if (1==length && 'r'==action[0]) { // read data
369  if (!perform) {
370  return true;
371  }
372  for (uint32_t i=0; i<repetition; i++) {
373  busvoodoo_uart_read(); // read from UART
374  }
375  } else if (1==length && 'u'==action[0]) { // sleep us
376  if (!perform) {
377  return true;
378  }
379  printf("wait for %u us\n", repetition);
380  sleep_us(repetition); // sleep
381  } else if (1==length && 'm'==action[0]) { // sleep ms
382  if (!perform) {
383  return true;
384  }
385  printf("wait for %u ms\n", repetition);
386  sleep_ms(repetition); // sleep
387  } else if ('0'==action[0]) { // send digit
388  if (1==length) { // just send 0
389  if (!perform) {
390  return true;
391  }
392  for (uint32_t i=0; i<repetition; i++) {
393  busvoodoo_uart_write(0); // write to UART
394  }
395  } else if ('x'==action[1] || 'b'==action[1]) { // send hex/binary
396  return busvoodoo_uart_action(action+1, repetition, perform); // just retry without leading 0
397  } else if (action[1]>='0' && action[1]<='9') { // send decimal
398  return busvoodoo_uart_action(action+1, repetition, perform); // just retry without leading 0
399  } else { // malformed action
400  return false;
401  }
402  } else if ('x'==action[0] && length>1) { // send hexadecimal value
403  for (uint32_t i=1; i<length; i++) { // check string
404  if (!((action[i]>='0' && action[i]<='9') || (action[i]>='a' && action[i]<='f') || (action[i]>='A' && action[i]<='F'))) { // check for hexadecimal character
405  return false; // not an hexadecimal string
406  }
407  }
408  if (!perform) {
409  return true;
410  }
411  uint32_t value = strtol(&action[1], NULL, 16); // get hex value
412  for (uint32_t i=0; i<repetition; i++) {
413  busvoodoo_uart_write(value); // write to SPI
414  }
415  } else if ('b'==action[0] && length>1) { // send binary value
416  for (uint32_t i=1; i<length; i++) { // check string
417  if (action[i]<'0' || action[i]>'1') { // check for binary character
418  return false; // not a binary string
419  }
420  }
421  if (!perform) {
422  return true;
423  }
424  uint32_t value = strtol(&action[1], NULL, 2); // get binary value
425  for (uint32_t i=0; i<repetition; i++) {
426  busvoodoo_uart_write(value); // write to SPI
427  }
428  } else if (action[0]>='1' && action[0]<='9') { // send decimal value
429  for (uint32_t i=1; i<length; i++) { // check string
430  if (action[i]<'0' || action[i]>'9') { // check for decimal character
431  return false; // not a decimal string
432  }
433  }
434  if (!perform) {
435  return true;
436  }
437  uint32_t value = strtol(&action[0], NULL, 10); // get decimal value
438  for (uint32_t i=0; i<repetition; i++) {
439  busvoodoo_uart_write(value); // write to SPI
440  }
441  } else if (length>=2 && ('"'==action[0] || '\''==action[0]) && (action[length-1]==action[0])) { // send ASCII character
442  if (!perform) {
443  return true;
444  }
445  for (uint32_t r=0; r<repetition; r++) {
446  for (uint32_t i=1; i<length-1; i++) { // go through string
447  busvoodoo_uart_write(action[i]); // write to SPI
448  }
449  }
450  } else { // malformed action
451  return false;
452  }
453  return true; // all went well
454 }
455 
456 
457 // command handlers
458 
462 static void busvoodoo_uart_command_actions(void* argument)
463 {
464  if (NULL==argument || 0==strlen(argument)) {
465  printf("available actions (separated by space or ,):\n");
466  printf("0\twrite decimal value\n");
467  printf("0x0\twrite hexadecimal value\n");
468  printf("0b0\twrite binary value\n");
469  printf("\"a\"/'a'\twrite ASCII characters\n");
470  printf("r\tread value\n");
471  printf("u/m\twait 1 us/ms\n");
472  printf(":n\trepeat action n times\n");
473  return;
474  }
475 
476  // copy argument since it will be modified
477  char* copy = calloc(strlen(argument)+1, sizeof(char));
478  if (!copy) {
479  while (true);
480  }
481  strncpy(copy, argument, strlen(argument)+1);
482  // verify and perform actions
483  if (!busvoodoo_global_actions(copy, false, &busvoodoo_uart_action)) { // verify actions
484  printf("malformed action(s)\n");
485  } else { // action are ok
486  printf("press any key to exit\n");
487  busvoodoo_global_actions(argument, true, &busvoodoo_uart_action); // perform action
488  if (user_input_available) { // user interrupted flow
489  user_input_get(); // discard user input
490  }
491  }
492  free(copy); // release memory
493 }
494 
498 static void busvoodoo_uart_command_transmit(void* argument)
499 {
500  if (NULL==argument || 0==strlen(argument)) { // nothing to transmit
501  argument = "\r\n"; // transmit CR+LF
502  }
503  printf("press any key to exit\n");
504  for (uint16_t i=0; ((char*)(argument))[i] && !user_input_available; i++) {
505  while ((0==(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
506  if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character
507  printf("%c", ((char*)(argument))[i]); // echo character to transmit
508  busvoodoo_led_red_pulse(BUSVOODOO_LED_PULSE); // pulse red LED to show transmission
509  usart_send(USART(BUSVOODOO_USART_ID), ((char*)(argument))[i]); // transmit character
510  }
511  }
512  if (user_input_available) { // user interrupted flow
513  user_input_get(); // discard user input
514  }
515  if (strcmp(argument, "\r\n")) {
516  printf("\n");
517  }
518 }
519 
523 static void busvoodoo_uart_command_receive(void* argument)
524 {
525  bool display_hex = false; // display in hex
526  bool display_bin = false; // display in bin
527  if (NULL!=argument && strlen(argument)>0) {
528  if (0==strcmp(argument, "h") || 0==strcmp(argument, "hex")) { // user wants hexadecimal display
529  display_hex = true; // remember to display in hexadecimal
530  } else if (0==strcmp(argument, "b") || 0==strcmp(argument, "bin")) { // user wants binary display
531  display_bin = true; // remember to display in binary
532  } else {
533  printf("malformed argument\n");
534  return;
535  }
536  }
537  printf("press any key to exit\n");
538  while (!user_input_available) { // check for user input to exit
539  if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received
540  uint16_t c = usart_recv(USART(BUSVOODOO_USART_ID)); // receive character
541  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
542  // remove unused bits (ignore parity bit)
543  if (USART_PARITY_NONE==busvoodoo_uart_parity) { // no parity bit in frame
544  if (8==busvoodoo_uart_databits) { // 8-bit frame
545  c &= 0xff;
546  } else { // 9-bit frame
547  c &= 0x1ff;
548  }
549  } else { // MSb is parity bit
550  if (8==busvoodoo_uart_databits) { // 8-bit frame
551  c &= 0x7f;
552  } else { // 9-bit frame
553  c &= 0xff;
554  }
555  }
556  if (display_hex) { // display data in hex
557  if ((USART_PARITY_NONE==busvoodoo_uart_parity) && 9==busvoodoo_uart_databits) { // case where the final data is 9 bits long
558  printf("%03x ", c);
559  } else {
560  printf("%02x ", c);
561  }
562  } else if (display_bin) { // display data in binary
563  if (USART_PARITY_NONE==busvoodoo_uart_parity) {
564  if (8==busvoodoo_uart_databits) { // 8-bit frame
565  printf("%08b ", c);
566  } else { // 9-bit frame
567  printf("%09b ", c);
568  }
569  } else { // one bit is a parity bit
570  if (8==busvoodoo_uart_databits) { // 8-bit frame
571  printf("%07b ", c);
572  } else { // 9-bit frame
573  printf("%08b ", c);
574  }
575  }
576  } else { // display in ASCII
577  printf("%c", c); // print received character
578  }
579  }
580  }
581  user_input_get(); // discard user input
582  printf("\n"); // get to next line
583 }
584 
588 static void busvoodoo_uart_command_transceive(void* argument)
589 {
590  (void)argument; // we won't use the argument
591  printf("press 5 times escape to exit\n");
592  char last_c = 0; // last user character received
593  uint8_t esc_count = 0; // number of times escape has press received
594  while (true) { // check for escape sequence
595  if (user_input_available) { // check if user wants to transmit something
596  char c = user_input_get(); // get user input
597  if (0x1b==c) { // user pressed escape
598  if (0x1b!=last_c) { // this is the first escape press
599  esc_count = 0;
600  }
601  esc_count++; // increment escape count
602  }
603  last_c = c; // remember key press
604  if (esc_count<5) { // check for escape sequence
605  while ((0==(USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
606  if (USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_TXE) { // we can send a character
607  usart_send_blocking(USART(BUSVOODOO_USART_ID), c); // send user character
608  busvoodoo_led_red_pulse(BUSVOODOO_LED_PULSE); // enable red LED to show transmission
609  }
610  } else { // user wants to exit
611  break; // exit infinite loop
612  }
613  }
614  if ((USART_SR(USART(BUSVOODOO_USART_ID)) & USART_SR_RXNE)) { // verify if data has been received
615  char c = usart_recv(USART(BUSVOODOO_USART_ID)); // receive character
616  busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
617  printf("%c", c); // print received character
618  }
619  }
620  printf("\n"); // get to next line
621 }
622 
626 static void busvoodoo_uart_command_error(void* argument)
627 {
628  (void)argument; // argument not used
629  printf("press any key to exit\n");
630  while (!user_input_available) { // wait until user interrupt
631  busvoodoo_uart_read(); // read incoming data (this also checks for errors
632  }
633  user_input_get(); // discard user input
634 }
635 
637 static const struct menu_command_t busvoodoo_uart_commands[] = {
638  {
639  'a',
640  "action",
641  "perform protocol actions",
643  "[actions]",
645  },
646  {
647  'r',
648  "receive",
649  "show incoming data [in hexadecimal or binary]",
651  "[hex|bin]",
653  },
654  {
655  't',
656  "transmit",
657  "transmit ASCII text (empty for CR+LF)",
659  "[text]",
661  },
662  {
663  'x',
664  "transceive",
665  "transmit and receive data",
667  NULL,
669  },
670  {
671  'e',
672  "error",
673  "verify incoming transmission for errors",
675  NULL,
677  },
678 };
679 
681  "uart",
682  "Universal Asynchronous Receiver-Transmitter",
687 };
#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
void busvoodoo_led_red_pulse(uint16_t ms)
pulse red LED for short duration
static bool busvoodoo_uart_pullup
if embedded pull-up resistors are used
static void busvoodoo_uart_command_actions(void *argument)
command to perform actions
void led_off(void)
switch off board LED
Definition: global.c:82
#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
global definitions and methods (API)
void busvoodoo_led_blue_pulse(uint16_t ms)
pulse blue LED for short duration
void sleep_us(uint32_t duration)
go to sleep for some microseconds
Definition: global.c:144
static bool busvoodoo_uart_setup(char **prefix, const char *line)
setup UART mode
#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
#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.
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:187
char busvoodoo_global_string[64]
shared string buffer, i.e.
struct busvoodoo_mode_t busvoodoo_uart_mode
UART mode interface definition.
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:159
#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:37
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