busvoodoo_uart_generic.c
Go to the documentation of this file.
/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdlib.h> // standard utilities
#include <string.h> // string utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/cm3/nvic.h> // interrupt utilities
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/usart.h> // USART utilities
#include <libopencm3/stm32/timer.h> // timer library
/* own libraries */
#include "global.h" // board definitions
#include "print.h" // printing utilities
#include "interrupt.h" // user interrupt table
#include "menu.h" // menu definitions
#include "usart_enhanced.h" // utilities for USART enhancements
#include "busvoodoo_global.h" // BusVoodoo definitions
#include "busvoodoo_uart_generic.h" // own definitions
static const struct busvoodoo_uart_generic_specific_t* busvoodoo_uart_generic_specific = NULL;
static enum busvoodoo_uart_generic_setting_t {
BUSVOODOO_UART_SETTING_NONE,
BUSVOODOO_UART_SETTING_BAUDRATE,
BUSVOODOO_UART_SETTING_DATABITS,
BUSVOODOO_UART_SETTING_PARITY,
BUSVOODOO_UART_SETTING_STOPBITS,
BUSVOODOO_UART_SETTING_HWFLOWCTL,
BUSVOODOO_UART_SETTING_DRIVE,
BUSVOODOO_UART_SETTING_DONE,
} busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE;
static uint32_t busvoodoo_uart_generic_baudrate = 115200;
static uint8_t busvoodoo_uart_generic_databits = 8;
static enum usart_enhanced_parity_t busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_NONE;
static uint32_t busvoodoo_uart_generic_stopbits = USART_STOPBITS_1;
static bool busvoodoo_uart_generic_hwflowctl = false;
static bool busvoodoo_uart_generic_drive = true;
static bool busvoodoo_uart_generic_pullup = false;
#define BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE false
bool busvoodoo_uart_generic_configure(const struct busvoodoo_uart_generic_specific_t* conf)
{
busvoodoo_uart_generic_specific = NULL; // reset specific information
if (NULL==conf) {
return false;
}
if (!conf->usart || !conf->usart_rcc || !conf->usart_rst) {
return false;
}
if (!conf->tx_rcc || !conf->rx_rcc) {
return false;
}
if (conf->hwflowctl && (!conf->rts_rcc || !conf->cts_rcc)) {
return false;
}
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))) {
return false;
}
busvoodoo_uart_generic_specific = conf;
return true;
}
bool busvoodoo_uart_generic_setup(char** prefix, const char* line)
{
if (NULL==busvoodoo_uart_generic_specific) { // there is nothing to configure
return true;
}
bool complete = false; // is the setup complete
if (NULL==line) { // first call
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // re-start configuration
}
switch (busvoodoo_uart_generic_setting) {
case BUSVOODOO_UART_SETTING_NONE:
snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "baud rate in bps (1200-2000000) [%u]", busvoodoo_uart_generic_baudrate);
*prefix = busvoodoo_global_string; // ask for baud rate
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_BAUDRATE;
break;
case BUSVOODOO_UART_SETTING_BAUDRATE:
if (NULL==line || 0==strlen(line)) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting
} else { // setting provided
uint32_t baudrate = atoi(line); // parse setting
if (baudrate>0 && baudrate<=2000000) { // check setting
busvoodoo_uart_generic_baudrate = baudrate; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DATABITS; // go to next setting
}
}
if (BUSVOODOO_UART_SETTING_DATABITS==busvoodoo_uart_generic_setting) { // if next setting
snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "data bits (5-8) [%u]", busvoodoo_uart_generic_databits); // prepare next setting
*prefix = busvoodoo_global_string; // display next setting
}
break;
case BUSVOODOO_UART_SETTING_DATABITS:
if (NULL==line || 0==strlen(line)) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting
} else if (1==strlen(line)) { // setting provided
uint8_t databits = atoi(line); // parse setting
if (databits>=5 && databits<=8) { // check setting
busvoodoo_uart_generic_databits = databits; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_PARITY; // go to next setting
}
}
if (BUSVOODOO_UART_SETTING_PARITY==busvoodoo_uart_generic_setting) { // if next setting
printf("1) none\n");
printf("2) even\n");
printf("3) odd\n");
printf("4) mark\n");
printf("5) space\n");
snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "parity (1,2,3,4,5) [%u]", busvoodoo_uart_generic_parity+1); // prepare next setting
*prefix = busvoodoo_global_string; // display next setting
}
break;
case BUSVOODOO_UART_SETTING_PARITY:
if (NULL==line || 0==strlen(line)) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
} else if (1==strlen(line)) { // setting provided
uint8_t parity = atoi(line); // parse setting
if (parity>0 && parity<6) { // check setting
busvoodoo_uart_generic_parity = parity-1;
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_STOPBITS; // go to next setting
}
}
if (BUSVOODOO_UART_SETTING_STOPBITS==busvoodoo_uart_generic_setting) { // if next setting
printf("1) 0.5\n");
printf("2) 1\n");
printf("3) 1.5\n");
printf("4) 2\n");
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
*prefix = busvoodoo_global_string; // display next setting
}
break;
case BUSVOODOO_UART_SETTING_STOPBITS:
if (NULL==line || 0==strlen(line)) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
} else if (1==strlen(line)) { // setting provided
if ('1'==line[0]) { // 0.5 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_0_5; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
} else if ('2'==line[0]) { // 1 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_1; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
} else if ('3'==line[0]) { // 1.5 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_1_5; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
} else if ('4'==line[0]) { // 2 stop bits
busvoodoo_uart_generic_stopbits = USART_STOPBITS_2; // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_HWFLOWCTL; // go to next setting
}
}
if (BUSVOODOO_UART_SETTING_HWFLOWCTL==busvoodoo_uart_generic_setting) { // if next setting
if (!busvoodoo_uart_generic_specific->hwflowctl) { // hardware flow control is not supported
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
goto setting_drive; // actually go to next setting
}
printf("1) no flow control\n");
printf("2) RTS/CTS hardware flow control\n");
snprintf(busvoodoo_global_string, LENGTH(busvoodoo_global_string), "flow control (1,2) [%c]", busvoodoo_uart_generic_hwflowctl ? '2' : '1'); // prepare next setting
*prefix = busvoodoo_global_string; // display next setting
}
break;
case BUSVOODOO_UART_SETTING_HWFLOWCTL:
if (NULL==line || 0==strlen(line)) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
} else if (1==strlen(line)) { // setting provided
if ('1'==line[0] || '2'==line[0]) { // setting provided
busvoodoo_uart_generic_hwflowctl = ('2'==line[0]); // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DRIVE; // go to next setting
}
}
setting_drive:
if (BUSVOODOO_UART_SETTING_DRIVE==busvoodoo_uart_generic_setting) { // if next setting
if (!busvoodoo_uart_generic_specific->multidrive) {
busvoodoo_uart_generic_drive = true; // only push-pull driving mode is supported
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
goto setting_done; // actually go to next setting
}
printf("1) push-pull (3.3V)\n");
printf("2) open-drain, with embedded pull-up resistors (2kO)\n");
printf("3) open-drain, with external pull-up resistors\n");
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
*prefix = busvoodoo_global_string; // display next setting
}
break;
case BUSVOODOO_UART_SETTING_DRIVE:
if (NULL==line || 0==strlen(line)) { // use default setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
} else if (1==strlen(line)) { // setting provided
uint8_t drive = atoi(line); // parse setting
if (1==drive || 2==drive || 3==drive) { // check setting
busvoodoo_uart_generic_drive = (1==drive); // remember setting
busvoodoo_uart_generic_pullup = (2==drive); // remember setting
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_DONE; // go to next setting
}
}
setting_done:
if (BUSVOODOO_UART_SETTING_DONE==busvoodoo_uart_generic_setting) { // we have all settings, configure UART
rcc_periph_clock_enable(RCC_AFIO); // enable clock for USART alternate function
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->usart_rcc); // enable clock for USART peripheral
rcc_periph_reset_pulse(busvoodoo_uart_generic_specific->usart_rst); // reset USART peripheral
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_baudrate); // set baud rate
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
usart_set_stopbits(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_stopbits); // set stop bits
if (busvoodoo_uart_generic_specific->hwflowctl && busvoodoo_uart_generic_hwflowctl) {
usart_set_flow_control(busvoodoo_uart_generic_specific->usart, USART_FLOWCONTROL_RTS_CTS); // set RTS/CTS flow control
} else {
usart_set_flow_control(busvoodoo_uart_generic_specific->usart, USART_FLOWCONTROL_NONE); // set no flow control
}
usart_set_mode(busvoodoo_uart_generic_specific->usart, USART_MODE_TX_RX); // full-duplex communication
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->tx_rcc); // enable clock for USART GPIO peripheral
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->rx_rcc); // enable clock for USART GPIO peripheral
if (busvoodoo_uart_generic_specific->hwflowctl && busvoodoo_uart_generic_hwflowctl) {
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->rts_rcc); // enable clock for USART GPIO peripheral
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->cts_rcc); // enable clock for USART GPIO peripheral
}
if (busvoodoo_uart_generic_drive) { // use push-pull drive mode
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
gpio_set(busvoodoo_uart_generic_specific->rx_port, busvoodoo_uart_generic_specific->rx_pin); // pull up to avoid noise when not connected
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
if (busvoodoo_uart_generic_specific->hwflowctl && busvoodoo_uart_generic_hwflowctl) { // use open drain drive mode
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
gpio_set(busvoodoo_uart_generic_specific->cts_port, busvoodoo_uart_generic_specific->cts_pin); // pull up to block transmission unless requested
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
}
} else {
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
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
if (busvoodoo_uart_generic_specific->hwflowctl && busvoodoo_uart_generic_hwflowctl) {
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
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
}
}
if (!busvoodoo_uart_generic_drive && busvoodoo_uart_generic_pullup) { // enable embedded pull-ups if used
busvoodoo_embedded_pullup(true); // set embedded pull-ups
printf("use LV to set pull-up voltage\n");
}
usart_enable(busvoodoo_uart_generic_specific->usart); // enable USART
// setup timer to measure RX edge timing for baud rate guessing
if (busvoodoo_uart_generic_specific->timer) {
}
busvoodoo_led_blue_off(); // disable blue LED because there is no activity
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
complete = true; // configuration is complete
}
break;
default: // unknown case
busvoodoo_uart_generic_setting = BUSVOODOO_UART_SETTING_NONE; // restart settings next time
break;
}
return complete;
}
void busvoodoo_uart_generic_exit(void)
{
if (NULL==busvoodoo_uart_generic_specific) {
return;
}
usart_disable(busvoodoo_uart_generic_specific->usart); // disable USART
rcc_periph_clock_disable(busvoodoo_uart_generic_specific->usart_rcc); // disable domain clock
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
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
if (busvoodoo_uart_generic_specific->hwflowctl) {
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
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
}
if (busvoodoo_uart_generic_specific->multidrive) {
busvoodoo_embedded_pullup(false); // disable embedded pull-ups
}
busvoodoo_uart_generic_specific = NULL; // remove specific information
}
static void busvoodoo_uart_generic_write(uint8_t value)
{
if (NULL==busvoodoo_uart_generic_specific) {
return;
}
if (busvoodoo_uart_generic_specific->tx_pre) {
(*busvoodoo_uart_generic_specific->tx_pre)();
}
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)
if ((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TXE) { // we can send data
// send data
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show transmission
usart_enhanced_send(busvoodoo_uart_generic_specific->usart, value); // transmit data
// display data send
printf("write: '%c'/0x%02x\n", value, value);
}
while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TC) && !user_input_available)); // wait for transfer to be complete
if (user_input_available) { // user interrupted flow
user_input_get(); // discard user input
}
if (busvoodoo_uart_generic_specific->tx_post) {
(*busvoodoo_uart_generic_specific->tx_post)();
}
}
static void busvoodoo_uart_generic_read(void)
{
if (NULL==busvoodoo_uart_generic_specific) {
return;
}
if (busvoodoo_uart_generic_specific->rx_pre) {
(*busvoodoo_uart_generic_specific->rx_pre)();
}
printf("read: ");
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)
if ((USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE)) { // verify if data has been received
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
// get the errors
bool error_noise = (0!=(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_NE)); // read noise error flag
bool error_framing = (0!=(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_FE)); // read frame error flag
uint8_t input = usart_enhanced_recv(busvoodoo_uart_generic_specific->usart); // read received character (also clears the error flags)
// display data
printf("'%c'/0x%02x", input, input);
// display errors
printf("(");
if (error_noise) {
printf("noise");
} else if (error_framing) {
printf("framing");
} else if (usart_enhanced_parity_error(busvoodoo_uart_generic_specific->usart)) {
printf("parity");
} else {
printf("no");
}
printf(" error)");
}
printf("\n");
if (user_input_available) { // user interrupted flow
user_input_get(); // discard user input
}
if (busvoodoo_uart_generic_specific->rx_post) {
(*busvoodoo_uart_generic_specific->rx_post)();
}
}
static bool busvoodoo_uart_generic_action(const char* action, uint32_t repetition, bool perform)
{
uint32_t length = strlen(action); // remember length since it will be used a number of times
if (NULL==action || 0==length) { // there is nothing to do
return true;
}
if (1==length && 'r'==action[0]) { // read data
if (!perform) {
return true;
}
for (uint32_t i=0; i<repetition; i++) {
busvoodoo_uart_generic_read(); // read from UART
}
} else if (1==length && 'u'==action[0]) { // sleep us
if (!perform) {
return true;
}
printf("wait for %u us\n", repetition);
sleep_us(repetition); // sleep
} else if (1==length && 'm'==action[0]) { // sleep ms
if (!perform) {
return true;
}
printf("wait for %u ms\n", repetition);
sleep_ms(repetition); // sleep
} else if ('0'==action[0]) { // send digit
if (1==length) { // just send 0
if (!perform) {
return true;
}
for (uint32_t i=0; i<repetition; i++) {
busvoodoo_uart_generic_write(0); // write to UART
}
} else if ('x'==action[1] || 'b'==action[1]) { // send hex/binary
return busvoodoo_uart_generic_action(action+1, repetition, perform); // just retry without leading 0
} else if (action[1]>='0' && action[1]<='9') { // send decimal
return busvoodoo_uart_generic_action(action+1, repetition, perform); // just retry without leading 0
} else { // malformed action
return false;
}
} else if ('x'==action[0] && length>1) { // send hexadecimal value
for (uint32_t i=1; i<length; i++) { // check string
if (!((action[i]>='0' && action[i]<='9') || (action[i]>='a' && action[i]<='f') || (action[i]>='A' && action[i]<='F'))) { // check for hexadecimal character
return false; // not an hexadecimal string
}
}
if (!perform) {
return true;
}
uint32_t value = strtol(&action[1], NULL, 16); // get hex value
for (uint32_t i=0; i<repetition; i++) {
busvoodoo_uart_generic_write(value); // write to SPI
}
} else if ('b'==action[0] && length>1) { // send binary value
for (uint32_t i=1; i<length; i++) { // check string
if (action[i]<'0' || action[i]>'1') { // check for binary character
return false; // not a binary string
}
}
if (!perform) {
return true;
}
uint32_t value = strtol(&action[1], NULL, 2); // get binary value
for (uint32_t i=0; i<repetition; i++) {
busvoodoo_uart_generic_write(value); // write to SPI
}
} else if (action[0]>='1' && action[0]<='9') { // send decimal value
for (uint32_t i=1; i<length; i++) { // check string
if (action[i]<'0' || action[i]>'9') { // check for decimal character
return false; // not a decimal string
}
}
if (!perform) {
return true;
}
uint32_t value = strtol(&action[0], NULL, 10); // get decimal value
for (uint32_t i=0; i<repetition; i++) {
busvoodoo_uart_generic_write(value); // write to SPI
}
} else if (length>=2 && ('"'==action[0] || '\''==action[0]) && (action[length-1]==action[0])) { // send ASCII character
if (!perform) {
return true;
}
for (uint32_t r=0; r<repetition; r++) {
for (uint32_t i=1; i<length-1; i++) { // go through string
busvoodoo_uart_generic_write(action[i]); // write to SPI
}
}
} else { // malformed action
return false;
}
return true; // all went well
}
// command handlers
static void busvoodoo_uart_generic_command_actions(void* argument)
{
if (NULL==argument || 0==strlen(argument)) {
printf("available actions (separated by space or ,):\n");
printf("0\twrite decimal value\n");
printf("0x0\twrite hexadecimal value\n");
printf("0b0\twrite binary value\n");
printf("\"a\"/'a'\twrite ASCII characters\n");
printf("r\tread value\n");
printf("u/m\twait 1 us/ms\n");
printf(":n\trepeat action n times\n");
return;
}
// copy argument since it will be modified
char* copy = calloc(strlen(argument)+1, sizeof(char));
if (!copy) {
while (true);
}
strncpy(copy, argument, strlen(argument)+1);
// verify and perform actions
if (!busvoodoo_global_actions(copy, false, &busvoodoo_uart_generic_action)) { // verify actions
printf("malformed action(s)\n");
} else { // action are ok
printf("press any key to exit\n");
busvoodoo_global_actions(argument, true, &busvoodoo_uart_generic_action); // perform action
if (user_input_available) { // user interrupted flow
user_input_get(); // discard user input
}
}
free(copy); // release memory
}
static void busvoodoo_uart_generic_command_transmit(void* argument)
{
if (NULL==busvoodoo_uart_generic_specific) {
return;
}
if (busvoodoo_uart_generic_specific->tx_pre) {
(*busvoodoo_uart_generic_specific->tx_pre)();
}
if (NULL==argument || 0==strlen(argument)) { // nothing to transmit
argument = "\r\n"; // transmit CR+LF
}
printf("press any key to exit\n");
for (uint16_t i=0; ((char*)(argument))[i] && !user_input_available; i++) {
while ((0==(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) { // we can send a character
printf("%c", ((char*)(argument))[i]); // echo character to transmit
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // pulse blue LED to show transmission
usart_enhanced_send(busvoodoo_uart_generic_specific->usart, ((char*)(argument))[i]); // transmit character
}
}
while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TC) && !user_input_available)); // wait for transfer to be complete
if (user_input_available) { // user interrupted flow
user_input_get(); // discard user input
}
if (strcmp(argument, "\r\n")) {
printf("\n");
}
if (busvoodoo_uart_generic_specific->tx_post) {
(*busvoodoo_uart_generic_specific->tx_post)();
}
}
static void busvoodoo_uart_generic_command_receive(void* argument)
{
bool display_hex = false; // display in hex
bool display_bin = false; // display in bin
if (NULL!=argument && strlen(argument)>0) {
if (0==strcmp(argument, "h") || 0==strcmp(argument, "hex")) { // user wants hexadecimal display
display_hex = true; // remember to display in hexadecimal
} else if (0==strcmp(argument, "b") || 0==strcmp(argument, "bin")) { // user wants binary display
display_bin = true; // remember to display in binary
} else {
printf("malformed argument\n");
return;
}
}
if (NULL==busvoodoo_uart_generic_specific) {
return;
}
if (busvoodoo_uart_generic_specific->rx_pre) {
(*busvoodoo_uart_generic_specific->rx_pre)();
}
printf("press any key to exit\n");
while (!user_input_available) { // check for user input to exit
if ((USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE)) { // verify if data has been received
uint8_t input = usart_enhanced_recv(busvoodoo_uart_generic_specific->usart); // receive character
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
if (display_hex) { // display data in hex
printf("%02x ", input);
} else if (display_bin) { // display data in binary
printf("%08b ", input);
} else { // display in ASCII
printf("%c", input); // print received character
}
}
}
user_input_get(); // discard user input
printf("\n"); // get to next line
if (busvoodoo_uart_generic_specific->rx_post) {
(*busvoodoo_uart_generic_specific->rx_post)();
}
}
static void busvoodoo_uart_generic_command_transceive(void* argument)
{
(void)argument; // we won't use the argument
if (NULL==busvoodoo_uart_generic_specific) {
return;
}
if (busvoodoo_uart_generic_specific->rx_pre) {
(*busvoodoo_uart_generic_specific->rx_pre)();
}
printf("press 5 times escape to exit\n");
char last_c = 0; // last user character received
uint8_t esc_count = 0; // number of times escape has press received
while (true) { // check for escape sequence
if (user_input_available) { // check if user wants to transmit something
char c = user_input_get(); // get user input
if (0x1b==c) { // user pressed escape
if (0x1b!=last_c) { // this is the first escape press
esc_count = 0;
}
esc_count++; // increment escape count
}
last_c = c; // remember key press
if (esc_count<5) { // check for escape sequence
if (busvoodoo_uart_generic_specific->rx_post) {
(*busvoodoo_uart_generic_specific->rx_post)();
}
if (busvoodoo_uart_generic_specific->tx_pre) {
(*busvoodoo_uart_generic_specific->tx_pre)();
}
while ((0==(USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) && !user_input_available)); // wait for transmit buffer to be empty
if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_TXE) { // we can send a character
usart_enhanced_send(busvoodoo_uart_generic_specific->usart, c); // send user character
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show transmission
}
while ((0==((USART_SR(busvoodoo_uart_generic_specific->usart)) & USART_SR_TC) && !user_input_available)); // wait for transfer to be complete
if (busvoodoo_uart_generic_specific->tx_post) {
(*busvoodoo_uart_generic_specific->tx_post)();
}
if (busvoodoo_uart_generic_specific->rx_pre) {
(*busvoodoo_uart_generic_specific->rx_pre)();
}
} else { // user wants to exit
break; // exit infinite loop
}
}
if ((USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE)) { // verify if data has been received
char input = usart_enhanced_recv(busvoodoo_uart_generic_specific->usart); // receive character
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE); // enable blue LED to show reception
printf("%c", input); // print received character
}
}
printf("\n"); // get to next line
if (busvoodoo_uart_generic_specific->rx_post) {
(*busvoodoo_uart_generic_specific->rx_post)();
}
}
static void busvoodoo_uart_generic_command_error(void* argument)
{
(void)argument; // argument not used
printf("press any key to exit\n");
while (!user_input_available) { // wait until user interrupt
busvoodoo_uart_generic_read(); // read incoming data (this also checks for errors
}
user_input_get(); // discard user input
}
struct uart_configuration_t {
uint8_t databits;
bool databits_matching;
bool parity_even;
bool parity_odd;
bool parity_mark;
bool parity_space;
uint8_t parity_possibilities;
};
static void uart_configuration_reset(struct uart_configuration_t* configuration)
{
configuration->databits_matching = true;
configuration->parity_even = true;
configuration->parity_odd = true;
configuration->parity_mark = true;
configuration->parity_space = true;
configuration->parity_possibilities = 4;
}
volatile bool pulse_flag = false;
volatile uint32_t pulse_duration = UINT32_MAX;
#if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
static void timer_isr(void)
#else
void TIM_ISR(2)(void)
#endif
{
busvoodoo_led_blue_pulse(BUSVOODOO_LED_PULSE);
static uint32_t pulse = UINT32_MAX; // measured pulse duration (MAX is an invalid values)
if (timer_get_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF)) { // overflow update event happened
timer_clear_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF); // clear flag
if (pulse>(UINT32_MAX-0x10000)) { // we can't measure longer pulser (and baud rate < 0.017 bps make no sense)
pulse = UINT32_MAX; // invalidate measured pulse
} else {
pulse += 0x10000; // account for the 16-bit timer limit
}
}
if (timer_get_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccof)) { // capture overflow occurred
timer_clear_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccof); // clear flag
pulse = UINT32_MAX; // invalidate measured pulse
}
if (timer_get_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccif)) {
uint16_t edge = *busvoodoo_uart_generic_specific->timer_ccr; // retrieve captured value (clears flag)
if (UINT32_MAX!=pulse) { // only calculate pulse if previous edge is valid
pulse = ((pulse&0xffff0000)+edge)-(pulse&0xffff); // calculate pulse duration
if (pulse<pulse_duration) { // save new pulse duration if smaller
pulse_duration = pulse;
pulse_flag = true;
}
}
pulse = edge; // replace with current edge time
}
}
static void busvoodoo_uart_generic_command_detect(void* argument)
{
(void)argument; // argument not used
if (!busvoodoo_uart_generic_specific->timer) {
printf("baud rate detection not possible (no timer available)\n");
return;
}
printf("the more traffic is incoming, the better the detection\n");
printf("press any key to exit\n");
// setup USART to receive character
uint8_t uart_databits = 8; // start with 8 bits since this is the most common case (i.e. no additional parity bit is used)
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, 1200); // configure UART to pre-selected baud rate
usart_set_databits(busvoodoo_uart_generic_specific->usart, uart_databits); // configure UART to pre-selected data-bits
usart_set_stopbits(busvoodoo_uart_generic_specific->usart, USART_STOPBITS_1); // 1 stop-bits also complies to 2 stop-bits
usart_set_parity(busvoodoo_uart_generic_specific->usart, USART_PARITY_NONE); // get the raw data since we will do the parity check ourselves
// setup timer to generate/measure signal timing
if ((busvoodoo_uart_generic_specific->timer_port != busvoodoo_uart_generic_specific->rx_port) || (busvoodoo_uart_generic_specific->timer_pin != busvoodoo_uart_generic_specific->rx_pin)) {
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->timer_port_rcc); // enable clock for GPIO peripheral
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
}
rcc_periph_clock_enable(busvoodoo_uart_generic_specific->timer_rcc); // enable clock for timer peripheral
timer_reset(busvoodoo_uart_generic_specific->timer); // reset timer state
timer_disable_counter(busvoodoo_uart_generic_specific->timer); // disable timer to configure it
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
timer_set_prescaler(busvoodoo_uart_generic_specific->timer, 1-1); // don't use prescale so to get the most precise measurement
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
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
/* 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.
* sadly the STM32 F1 family timer peripheral does not supporting triggering on both edges at the same time (the F0 family can).
* 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).
* thus we will start the trigger on the start bit, and wait for the next falling edge.
* this way we don't need to change the parity and have twice the time to measure the baud rate.
* to calculate the baud rate we just have to divided the frequency by two.
* 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)
*/
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
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
timer_ic_enable(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_ic); // enable capture interrupt
timer_clear_flag(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_sr_ccif); // clear input compare flag
timer_enable_irq(busvoodoo_uart_generic_specific->timer, busvoodoo_uart_generic_specific->timer_dier_ccie); // enable capture interrupt
timer_update_on_overflow(busvoodoo_uart_generic_specific->timer); // only use counter overflow as UEV source (use overflow to measure longer times)
timer_clear_flag(busvoodoo_uart_generic_specific->timer, TIM_SR_UIF); // clear overflow flag
timer_enable_irq(busvoodoo_uart_generic_specific->timer, TIM_DIER_UIE); // enable update interrupt for timer
#if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
void (*isr_backup)(void) = interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq]; // backup current timer ISR before replacing it
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
bool irq_backup = nvic_get_irq_enabled(busvoodoo_uart_generic_specific->timer_nvic_irq); // backup enable IRQ setting
#endif
nvic_enable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // catch interrupts for this timer
pulse_duration = UINT32_MAX; // reset pulse duration
timer_enable_counter(busvoodoo_uart_generic_specific->timer); // enable timer
// switch on RX
if (busvoodoo_uart_generic_specific->rx_pre) {
(*busvoodoo_uart_generic_specific->rx_pre)();
}
bool reset_state = true; // flag to know if we need to reset the states
uint8_t rx_errors; // number of UART receive errors received
bool wait_for_idle = false; // flag to wait for an IDLE frame
struct uart_configuration_t uart_configurations[] = {
{ .databits = 5 },
{ .databits = 6 },
{ .databits = 8 },
{ .databits = 7 },
};
uint8_t uart_configuration_valid = LENGTH(uart_configurations); // current best valid UART configuration index
char uart_configuration_parity = '?'; // current best valid UART parity
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
uint32_t uart_baudrate = 0; // fastest found baud rate
while (!user_input_available) {
if (reset_state) { // reset the configuration
rx_errors = 0;
for (uint8_t i=0; i<LENGTH(uart_configurations); i++) {
uart_configuration_reset(&uart_configurations[i]);
}
usart_recv(busvoodoo_uart_generic_specific->usart); // clear input buffer and allow flag to be set
usart_enable(busvoodoo_uart_generic_specific->usart); // ensure UART is enabled
reset_state = false;
}
if (pulse_flag) { // new pulse duration has been measured
pulse_flag = false; // clear flag
printf("u");
uint32_t baudrate = rcc_ahb_frequency/(pulse_duration/2); // calculate baud rate based on measured timing
if (baudrate>uart_baudrate+100) { // new higher baud rate detected
uart_baudrate = baudrate; // save new baud rate
if (uart_baudrate>=1200) { // ensure minimum hardware supported baud rate is respected
// search for closest standard baud rate
uint32_t standard_baudrate = 0;
for (uint8_t i=0; i<LENGTH(baudrates); i++) {
if (uart_baudrate>=baudrates[i]*0.9 && uart_baudrate<=baudrates[i]*1.1) { // measured baud rate matches standard baud rate within factor
standard_baudrate = baudrates[i]; // remember matching baud rate
break; // stop searching for matching baud rate
}
}
if (standard_baudrate) { // matching standard baud rate found
uart_baudrate = standard_baudrate; // save matching baud rate
}
usart_disable(busvoodoo_uart_generic_specific->usart); // disable UART before reconfiguring
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, uart_baudrate); // set new baud rate
reset_state = true; // reset the states since we set a new baud rate
printf("\nnew baud rate: %u bps\n", uart_baudrate); // show measurement frequency
} else {
printf("\ndetected %u bps baud rate is lower than minimum supported 1200 bps\n", baudrate);
}
}
}
if (USART_SR(busvoodoo_uart_generic_specific->usart) & (USART_SR_NE|USART_SR_FE)) { // error on UART received
usart_recv(busvoodoo_uart_generic_specific->usart); // clear input buffer and flags
rx_errors++; // increment number of errors
if (rx_errors>=5) { // the format seems wrong
// the threshold must be high enough so the UART peripheral has enough opportunities to synchronize to the start bit (just after and idle frame)
// two high probable frame error causes:
// - 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
// - 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
uart_databits = ((8==uart_databits) ? 9 : 8); // switch between 8 and 9-bit packets
usart_disable(busvoodoo_uart_generic_specific->usart); // disable UART before reconfiguring
usart_set_databits(busvoodoo_uart_generic_specific->usart, uart_databits); // set new data width
reset_state = true;
pulse_duration = UINT32_MAX; // also reset the baud rate
uart_baudrate = 0; // also reset the baud rate
rx_errors = 0; // reset error counter
printf("\nrestarting guessing because too detected error\n");
} else {
wait_for_idle = true; // wait form an IDLE frame so to better sync to the next start bit
}
}
if (wait_for_idle) {
/* 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.
* 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).
* there this no workaround be it will be fixed when changing the baud rate
*/
if (USART_SR(busvoodoo_uart_generic_specific->usart) | USART_SR_IDLE) {
wait_for_idle = false;
}
if (USART_SR(busvoodoo_uart_generic_specific->usart) | USART_SR_RXNE) {
USART_DR(busvoodoo_uart_generic_specific->usart); // empty receive buffer so the IDLE flag can retrigger
}
}
if (USART_SR(busvoodoo_uart_generic_specific->usart) & USART_SR_RXNE) { // data received
uint16_t usart_data = usart_recv(busvoodoo_uart_generic_specific->usart); // save received data (also clears flag)
if (0 == uart_baudrate) { // we did not find any valid baud rate yet
continue;
}
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
uint16_t usart_data_relevant = usart_data & ~(0xffff << uart_configurations[uart_configuration_valid].databits); // get only the data bits
// verify parity and word size
for (uint8_t i = 0; i < LENGTH(uart_configurations); i++) {
// skip check if we already know the word size is wrong
if (!uart_configurations[i].databits_matching) {
continue;
}
// do parity checks
if (uart_configurations[i].parity_even) {
uart_configurations[i].parity_even &= usart_enhanced_even_parity_lut[usart_data_relevant];
}
if (uart_configurations[i].parity_odd) {
uart_configurations[i].parity_odd &= !usart_enhanced_even_parity_lut[usart_data_relevant];
}
if (uart_configurations[i].parity_mark) {
uart_configurations[i].parity_mark &= (usart_data_padded & (1 << uart_configurations[i].databits));
}
if (uart_configurations[i].parity_space) {
uart_configurations[i].parity_space &= !(usart_data_padded & (1 << uart_configurations[i].databits));
}
// update parity count
uart_configurations[i].parity_possibilities = 0;
if (uart_configurations[i].parity_even) {
uart_configurations[i].parity_possibilities++;
}
if (uart_configurations[i].parity_odd) {
uart_configurations[i].parity_possibilities++;
}
if (uart_configurations[i].parity_mark) {
uart_configurations[i].parity_possibilities++;
}
if (uart_configurations[i].parity_space) {
uart_configurations[i].parity_possibilities++;
}
// verify word size
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
if (~usart_data_padded & databits_mask) { // see if bit outside the word size are cleared
uart_configurations[i].databits_matching = false;
}
}
bool no_valid_configuration = true;
uint8_t new_valid_configuration = LENGTH(uart_configurations);
char parity = '?';
for (uint8_t i=0; i<LENGTH(uart_configurations); i++) {
// skip check the word size is wrong
if (!uart_configurations[i].databits_matching) {
continue;
}
no_valid_configuration = false;
if (uart_configurations[i].parity_possibilities > 1) { // parity is not yet clear
continue;
} else if (uart_configurations[i].parity_even) {
parity = 'E';
} else if (uart_configurations[i].parity_odd) {
parity = 'O';
} else if (uart_configurations[i].parity_mark) {
parity = 'M';
} else if (uart_configurations[i].parity_space) {
parity = 'S';
} else if (0==uart_configurations[i].parity_possibilities) {
parity = 'N';
}
new_valid_configuration = i;
break; // stop searching since we found a configuration
}
if (no_valid_configuration) {
reset_state = true; // reset the configurations
pulse_duration = UINT32_MAX; // also reset the baud rate
uart_baudrate = 0; // also reset the baud rate
} 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
uart_configuration_valid = new_valid_configuration;
uart_configuration_parity = parity;
printf("\nnew UART configuration found: %u %u%c1\n", uart_baudrate, uart_configurations[uart_configuration_valid].databits, uart_configuration_parity);
}
// print received data if a configuration has been found
if (uart_configuration_valid < LENGTH(uart_configurations)) { // valid configuration existing
if (uart_configurations[uart_configuration_valid].databits >= 7 && usart_data_relevant < 0x80) { // this is probably valid ASCII data
printf("%c", usart_data_relevant);
} else {
printf("0x%02x ", usart_data_relevant);
}
} else {
printf("0b%09b\n", usart_data_relevant);
}
}
}
user_input_get(); // clear input used to interrupt previous loop
printf("\n");
// switch off RX
if (busvoodoo_uart_generic_specific->rx_post) {
(*busvoodoo_uart_generic_specific->rx_post)();
}
// stop timer
timer_disable_counter(busvoodoo_uart_generic_specific->timer); // disable timer
#if defined(BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE) && BUSVOODOO_UART_TIMER_USE_INTERRUPT_TABLE
interrupt_table[busvoodoo_uart_generic_specific->timer_nvic_irq] = isr_backup; // restore ISR
if (!irq_backup) {
nvic_disable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // disable IRQ
}
#else
nvic_disable_irq(busvoodoo_uart_generic_specific->timer_nvic_irq); // disable IRQ
#endif
timer_reset(busvoodoo_uart_generic_specific->timer); // reset timer
rcc_periph_clock_disable(busvoodoo_uart_generic_specific->timer_rcc); // disable clock for timer peripheral
// no need to disable pin since it's already a floating input it is not Rx
if (uart_configuration_valid < LENGTH(uart_configurations)) {
printf("press y to use configuration found: %u %u%c1\n", uart_baudrate, uart_configurations[uart_configuration_valid].databits, uart_configuration_parity);
while (!user_input_available); // wait until user input
if ('y' == user_input_get()) { // user want to use found configuration
busvoodoo_uart_generic_baudrate = uart_baudrate;
busvoodoo_uart_generic_databits = uart_configurations[uart_configuration_valid].databits;
if (uart_configurations[uart_configuration_valid].parity_even) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_EVEN;
} else if (uart_configurations[uart_configuration_valid].parity_odd) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_ODD;
} else if (uart_configurations[uart_configuration_valid].parity_mark) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_MARK;
} else if (uart_configurations[uart_configuration_valid].parity_space) {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_SPACE;
} else {
busvoodoo_uart_generic_parity = USART_ENHANCED_PARITY_NONE;
}
busvoodoo_uart_generic_stopbits = USART_STOPBITS_1;
}
}
// reconfigure USART
usart_set_baudrate(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_baudrate); // set baud rate
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
usart_set_stopbits(busvoodoo_uart_generic_specific->usart, busvoodoo_uart_generic_stopbits); // set stop bits
}
const struct menu_command_t busvoodoo_uart_generic_commands[busvoodoo_uart_generic_commands_nb] = {
{
.shortcut = 'a',
.name = "action",
.command_description = "perform protocol actions",
.argument = MENU_ARGUMENT_STRING,
.argument_description = "[actions]",
.command_handler = &busvoodoo_uart_generic_command_actions,
},
{
.shortcut = 'r',
.name = "receive",
.command_description = "show incoming data [in hexadecimal or binary]",
.argument = MENU_ARGUMENT_STRING,
.argument_description = "[hex|bin]",
.command_handler = &busvoodoo_uart_generic_command_receive,
},
{
.shortcut = 't',
.name = "transmit",
.command_description = "transmit ASCII text (empty for CR+LF)",
.argument = MENU_ARGUMENT_STRING,
.argument_description = "[text]",
.command_handler = &busvoodoo_uart_generic_command_transmit,
},
{
.shortcut = 'x',
.name = "transceive",
.command_description = "transmit and receive data",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &busvoodoo_uart_generic_command_transceive,
},
{
.shortcut = 'e',
.name = "error",
.command_description = "verify incoming transmission for errors",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &busvoodoo_uart_generic_command_error,
},
{
.shortcut = 'd',
.name = "detect",
.command_description = "auto-detect serial configuration (baud rate, data bits, parity)",
.argument = MENU_ARGUMENT_NONE,
.argument_description = NULL,
.command_handler = &busvoodoo_uart_generic_command_detect,
},
};
busvoodoo_uart_generic_baudrate
static uint32_t busvoodoo_uart_generic_baudrate
UART baud rate (in bps)
Definition: busvoodoo_uart_generic.c:56
busvoodoo_uart_generic_specific_t::timer_pin
uint32_t timer_pin
pin of timer capture channel
Definition: busvoodoo_uart_generic.h:52
busvoodoo_uart_generic_setting
static enum busvoodoo_uart_generic_setting_t busvoodoo_uart_generic_setting
current mode setup stage
busvoodoo_uart_generic_setup
bool busvoodoo_uart_generic_setup(char **prefix, const char *line)
setup generic UART mode
Definition: busvoodoo_uart_generic.c:98
pulse_duration
volatile uint32_t pulse_duration
smallest pulse duration measured
Definition: busvoodoo_uart_generic.c:692
menu_command_t
command menu entry
Definition: menu.h:32
uart_configuration_t::parity_odd
bool parity_odd
if the date is still matching the additional odd parity bit
Definition: busvoodoo_uart_generic.c:672
BUSVOODOO_LED_PULSE
#define BUSVOODOO_LED_PULSE
recommended duration in ms for pulsing LEDs to show activity
Definition: busvoodoo_global.h:112
usart_enhanced_send
void usart_enhanced_send(uint32_t usart, uint8_t data)
send data over the enhanced USART using the configuration
Definition: usart_enhanced.c:96
uart_configuration_t
the possible properties of a UART configuration (to be updated with every character) ...
Definition: busvoodoo_uart_generic.c:668
busvoodoo_uart_generic_hwflowctl
static bool busvoodoo_uart_generic_hwflowctl
UART hardware flow control setting (true = with hardware flow control, false = without hardware flow ...
Definition: busvoodoo_uart_generic.c:64
busvoodoo_global.h
BusVoodoo global definitions and methods (API)
busvoodoo_uart_generic_action
static bool busvoodoo_uart_generic_action(const char *action, uint32_t repetition, bool perform)
perform UART action
Definition: busvoodoo_uart_generic.c:382
busvoodoo_uart_generic_setting_t
busvoodoo_uart_generic_setting_t
mode setup stage
Definition: busvoodoo_uart_generic.c:45
uart_configuration_t::parity_possibilities
uint8_t parity_possibilities
the number to still matching parity possibilities (number of parity_* at true)
Definition: busvoodoo_uart_generic.c:675
busvoodoo_uart_generic_specific_t::tx_post
void(* tx_post)(void)
method to be called after transmitting data
Definition: busvoodoo_uart_generic.h:33
busvoodoo_led_blue_pulse
void busvoodoo_led_blue_pulse(uint32_t ms)
pulse blue LED for short duration
Definition: busvoodoo_global.c:468
USART_ENHANCED_PARITY_SPACE
space parity
Definition: usart_enhanced.h:44
busvoodoo_uart_generic_specific_t::timer_ic
enum tim_ic_id timer_ic
timer input capture channel
Definition: busvoodoo_uart_generic.h:53
pulse_flag
volatile bool pulse_flag
set when a small pulse time is detected
Definition: busvoodoo_uart_generic.c:691
busvoodoo_uart_generic_specific_t::rx_pin
uint32_t rx_pin
GPIO pin address of receive pin.
Definition: busvoodoo_uart_generic.h:35
busvoodoo_uart_generic_specific_t::timer_port
uint32_t timer_port
port of timer capture channel
Definition: busvoodoo_uart_generic.h:50
global.h
global definitions and methods (API)
busvoodoo_uart_generic_specific_t::timer_dier_ccie
uint32_t timer_dier_ccie
timer channel capture interrupt enable
Definition: busvoodoo_uart_generic.h:58
busvoodoo_uart_generic_specific_t::timer_port_rcc
uint32_t timer_port_rcc
port RCC of timer capture channel
Definition: busvoodoo_uart_generic.h:51
busvoodoo_uart_generic_specific_t::rts_port
uint32_t rts_port
GPIO port address of request to send pin.
Definition: busvoodoo_uart_generic.h:41
busvoodoo_uart_generic_specific_t::timer_ccr
volatile uint32_t * timer_ccr
timer channel capture register
Definition: busvoodoo_uart_generic.h:57
sleep_us
void sleep_us(uint32_t duration)
go to sleep for some microseconds
Definition: global.c:102
busvoodoo_uart_generic_command_actions
static void busvoodoo_uart_generic_command_actions(void *argument)
command to perform actions
Definition: busvoodoo_uart_generic.c:482
busvoodoo_uart_generic_specific
static const struct busvoodoo_uart_generic_specific_t * busvoodoo_uart_generic_specific
the USART mode specific information
Definition: busvoodoo_uart_generic.c:43
busvoodoo_uart_generic_specific_t::timer
uint32_t timer
timer peripheral
Definition: busvoodoo_uart_generic.h:48
busvoodoo_uart_generic_write
static void busvoodoo_uart_generic_write(uint8_t value)
write to UART
Definition: busvoodoo_uart_generic.c:309
usart_enhanced_parity_t
usart_enhanced_parity_t
enhanced USART setting for the additional parity bit
Definition: usart_enhanced.h:26
busvoodoo_uart_generic_command_error
static void busvoodoo_uart_generic_command_error(void *argument)
command to verify incoming transmission for error
Definition: busvoodoo_uart_generic.c:657
busvoodoo_uart_generic_command_detect
static void busvoodoo_uart_generic_command_detect(void *argument)
command to auto-detect incoming UART configuration (baud rate, data bits, parity) ...
Definition: busvoodoo_uart_generic.c:732
TIM_ISR
void TIM_ISR(2)
timer ISR to measure edge timing
Definition: busvoodoo_uart_generic.c:698
busvoodoo_led_blue_off
void busvoodoo_led_blue_off(void)
switch off blue LED
Definition: busvoodoo_global.c:426
busvoodoo_uart_generic_specific_t::rx_pre
void(* rx_pre)(void)
method to be called before receiving data
Definition: busvoodoo_uart_generic.h:37
busvoodoo_uart_generic_specific_t::usart
uint32_t usart
USART peripheral base address.
Definition: busvoodoo_uart_generic.h:25
uart_configuration_t::parity_even
bool parity_even
if the date is still matching the additional even parity bit
Definition: busvoodoo_uart_generic.c:671
busvoodoo_uart_generic_specific_t::rx_rcc
uint32_t rx_rcc
GPIO RCC address of receive pin.
Definition: busvoodoo_uart_generic.h:36
busvoodoo_uart_generic_specific_t::multidrive
bool multidrive
if multiple drive modes are supported (push-pull, open-drain with internal resistors, open-drain with external resistors), or just push-pull
Definition: busvoodoo_uart_generic.h:28
busvoodoo_uart_generic_specific_t::rx_port
uint32_t rx_port
GPIO port address of receive pin.
Definition: busvoodoo_uart_generic.h:34
busvoodoo_uart_generic_specific_t::timer_rcc
uint32_t timer_rcc
timer RCC address
Definition: busvoodoo_uart_generic.h:49
busvoodoo_uart_generic_specific_t::tx_pin
uint32_t tx_pin
GPIO pin address of transmit pin.
Definition: busvoodoo_uart_generic.h:30
busvoodoo_uart_generic_specific_t::rx_post
void(* rx_post)(void)
method to be called after receiving data
Definition: busvoodoo_uart_generic.h:38
busvoodoo_uart_generic_exit
void busvoodoo_uart_generic_exit(void)
exit genetic UART mode
Definition: busvoodoo_uart_generic.c:287
menu_command_t::shortcut
char shortcut
short command code (0 if not available)
Definition: menu.h:33
interrupt_table
vector_table_entry_t interrupt_table[NVIC_IRQ_COUNT]
table of interrupts which can set to user functions
Definition: interrupt.c:21
busvoodoo_uart_generic_specific_t::tx_rcc
uint32_t tx_rcc
GPIO RCC address of transmit pin.
Definition: busvoodoo_uart_generic.h:31
busvoodoo_uart_generic_specific_t
UART specific methods that will be called by the generic methods.
Definition: busvoodoo_uart_generic.h:24
busvoodoo_uart_generic_specific_t::timer_sr_ccif
uint32_t timer_sr_ccif
timer channel capture interrupt flag
Definition: busvoodoo_uart_generic.h:55
busvoodoo_uart_generic_specific_t::tx_pre
void(* tx_pre)(void)
method to be called before transmitting data
Definition: busvoodoo_uart_generic.h:32
busvoodoo_uart_generic_pullup
static bool busvoodoo_uart_generic_pullup
if embedded pull-up resistors are used
Definition: busvoodoo_uart_generic.c:68
busvoodoo_uart_generic_specific_t::rts_rcc
uint32_t rts_rcc
GPIO RCC address of request to send pin.
Definition: busvoodoo_uart_generic.h:43
busvoodoo_uart_generic_command_transmit
static void busvoodoo_uart_generic_command_transmit(void *argument)
command to transmit a string
Definition: busvoodoo_uart_generic.c:518
busvoodoo_uart_generic_specific_t::cts_port
uint32_t cts_port
GPIO port address of clear to send pin.
Definition: busvoodoo_uart_generic.h:44
busvoodoo_uart_generic_parity
static enum usart_enhanced_parity_t busvoodoo_uart_generic_parity
UART parity setting.
Definition: busvoodoo_uart_generic.c:60
user_input_get
char user_input_get(void)
get user input
Definition: global.c:145
busvoodoo_global_string
char busvoodoo_global_string[64]
shared string buffer, i.e.
Definition: busvoodoo_global.c:72
busvoodoo_uart_generic_commands_nb
#define busvoodoo_uart_generic_commands_nb
number of commands supported by the generic UART mode
Definition: busvoodoo_uart_generic.h:79
interrupt.h
BusVoodoo runtime interrupt table.
busvoodoo_uart_generic_specific_t::usart_rst
uint32_t usart_rst
USART RST address.
Definition: busvoodoo_uart_generic.h:27
snprintf
size_t snprintf(char *str, size_t size, const char *format,...)
print format string on string or user output
Definition: print.c:458
busvoodoo_global_actions
bool busvoodoo_global_actions(char *actions, bool perform, bool(*action_handler)(const char *action, uint32_t repetition, bool perform))
parse and perform actions
Definition: busvoodoo_global.c:561
sleep_ms
void sleep_ms(uint32_t duration)
go to sleep for some milliseconds
Definition: global.c:117
busvoodoo_uart_generic_specific_t::rts_pin
uint32_t rts_pin
GPIO pin address of request to send pin.
Definition: busvoodoo_uart_generic.h:42
busvoodoo_uart_generic_configure
bool busvoodoo_uart_generic_configure(const struct busvoodoo_uart_generic_specific_t *conf)
provide the generic USART with mode specific information
Definition: busvoodoo_uart_generic.c:76
uart_configuration_t::databits
uint8_t databits
data word size in bits
Definition: busvoodoo_uart_generic.c:669
busvoodoo_uart_generic_specific_t::usart_rcc
uint32_t usart_rcc
USART RCC address.
Definition: busvoodoo_uart_generic.h:26
printf
size_t printf(const char *format,...)
print format string on user output
Definition: print.c:448
USART_ENHANCED_PARITY_EVEN
even parity
Definition: usart_enhanced.h:32
usart_enhanced_config
bool usart_enhanced_config(uint32_t usart, uint8_t databits, enum usart_enhanced_parity_t parity)
configure enhanced USART
Definition: usart_enhanced.c:68
busvoodoo_uart_generic_read
static void busvoodoo_uart_generic_read(void)
read from UART
Definition: busvoodoo_uart_generic.c:336
USART_ENHANCED_PARITY_NONE
no parity
Definition: usart_enhanced.h:28
busvoodoo_uart_generic.h
BusVoodoo generic UART mode (API)
busvoodoo_uart_generic_specific_t::timer_nvic_irq
uint32_t timer_nvic_irq
timer IRQ
Definition: busvoodoo_uart_generic.h:59
busvoodoo_uart_generic_stopbits
static uint32_t busvoodoo_uart_generic_stopbits
UART stop bits setting.
Definition: busvoodoo_uart_generic.c:62
busvoodoo_uart_generic_command_receive
static void busvoodoo_uart_generic_command_receive(void *argument)
command to receive data
Definition: busvoodoo_uart_generic.c:553
print.h
printing utilities to replace the large printf from the standard library (API)
busvoodoo_uart_generic_specific_t::timer_sr_ccof
uint32_t timer_sr_ccof
timer channel capture overrun flag
Definition: busvoodoo_uart_generic.h:56
busvoodoo_uart_generic_specific_t::cts_rcc
uint32_t cts_rcc
GPIO RCC address of clear to send pin.
Definition: busvoodoo_uart_generic.h:46
uart_configuration_t::databits_matching
bool databits_matching
if the data is still matching the data bits
Definition: busvoodoo_uart_generic.c:670
usart_enhanced_recv
uint8_t usart_enhanced_recv(uint32_t usart)
receive data over the enhanced USART using the configuration
Definition: usart_enhanced.c:131
busvoodoo_uart_generic_commands
const struct menu_command_t busvoodoo_uart_generic_commands[busvoodoo_uart_generic_commands_nb]
commands supported by the generic UART mode
Definition: busvoodoo_uart_generic.c:1018
busvoodoo_embedded_pullup
float busvoodoo_embedded_pullup(bool on)
enable embedded pull-up resistors
Definition: busvoodoo_global.c:373
busvoodoo_uart_generic_drive
static bool busvoodoo_uart_generic_drive
pin drive mode (true = push-pull, false = open-drain)
Definition: busvoodoo_uart_generic.c:66
uart_configuration_t::parity_space
bool parity_space
if the date is still matching the additional space parity bit
Definition: busvoodoo_uart_generic.c:674
USART_ENHANCED_PARITY_MARK
mark parity
Definition: usart_enhanced.h:40
LENGTH
#define LENGTH(x)
get the length of an array
Definition: global.h:26
busvoodoo_uart_generic_specific_t::hwflowctl
bool hwflowctl
if RTC/CTS hardware flow control is supported
Definition: busvoodoo_uart_generic.h:40
busvoodoo_uart_generic_command_transceive
static void busvoodoo_uart_generic_command_transceive(void *argument)
command to transmit and receive data
Definition: busvoodoo_uart_generic.c:597
uart_configuration_reset
static void uart_configuration_reset(struct uart_configuration_t *configuration)
reset all matching values of UART configuration
Definition: busvoodoo_uart_generic.c:681
uart_configuration_t::parity_mark
bool parity_mark
if the date is still matching the additional mark parity bit
Definition: busvoodoo_uart_generic.c:673
user_input_available
volatile bool user_input_available
flag set when user input is available
Definition: global.c:36
busvoodoo_uart_generic_databits
static uint8_t busvoodoo_uart_generic_databits
UART data bits.
Definition: busvoodoo_uart_generic.c:58
busvoodoo_uart_generic_specific_t::tx_port
uint32_t tx_port
GPIO port address of transmit pin.
Definition: busvoodoo_uart_generic.h:29
usart_enhanced_parity_error
bool usart_enhanced_parity_error(uint32_t usart)
get the parity status of the received data
Definition: usart_enhanced.c:172
usart_enhanced.h
library for enhanced USART communication (API)
busvoodoo_uart_generic_specific_t::cts_pin
uint32_t cts_pin
GPIO pin address of clear to send pin.
Definition: busvoodoo_uart_generic.h:45
USART_ENHANCED_PARITY_ODD
odd parity
Definition: usart_enhanced.h:36
busvoodoo_uart_generic_specific_t::timer_ic_in_ti
enum tim_ic_input timer_ic_in_ti
timer input capture channel TIn
Definition: busvoodoo_uart_generic.h:54
usart_enhanced_even_parity_lut
const bool usart_enhanced_even_parity_lut[256]
know if there is an even number of 1&#39;s in a integer
Definition: usart_enhanced.c:41