/** library for enhanced USART communication (code) * @file usart_enhanced.c * @author King Kévin * @copyright SPDX-License-Identifier: GPL-3.0-or-later * @date 2018 * @details the USART peripherals only support 8 or 9-bit word and even or odd parity (included in the data bits). The library adds support for 5 to 8-bit words, none/even/odd/mark/space parity (on top of the data bits) * @note since parity is handled in software, the parity error (PE) flag is unused and should be replaced by the value return by usart_enhanced_parity_error * @remark 9-bit raw communication is not supported since this is not common and can be done without this library */ /* standard libraries */ #include // standard integer types /* STM32 (including CM3) libraries */ #include // USART utilities /* own libraries */ #include "usart_enhanced.h" // utilities for USART enhancements /** number of available USART peripherals */ #define USART_PERIPHERALS_NB 3 /** configured enhanced USART word size */ static uint8_t usart_enhanced_databits[USART_PERIPHERALS_NB]; /** configured enhanced USART parity */ static enum usart_enhanced_parity_t usart_enhanced_parity[USART_PERIPHERALS_NB]; /** last enhanced USART parity error status */ static bool usart_enhanced_parity_errors[USART_PERIPHERALS_NB]; const bool usart_enhanced_even_parity_lut[256] = { true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, }; /** get index of corresponding enhanced USART configurations * @param[in] usart USART peripheral base address * @return index used for the individual USART configurations * @note the returned value is valid only is less than USART_PERIPHERALS_NB */ static uint8_t usart_enhanced_id(uint32_t usart) { uint8_t to_return = USART_PERIPHERALS_NB; switch (usart) { case USART1: to_return = 0; break; case USART2: to_return = 1; break; case USART3: to_return = 2; break; default: to_return = USART_PERIPHERALS_NB; break; } return to_return; } bool usart_enhanced_config(uint32_t usart, uint8_t databits, enum usart_enhanced_parity_t parity) { /* sanity check */ uint8_t id = usart_enhanced_id(usart); if (id>=USART_PERIPHERALS_NB) { return false; } if (databits<5 || databits>8) { return false; } if (parity>USART_ENHANCED_PARITY_SPACE) { return false; } // save configuration for later use usart_enhanced_databits[id] = databits; usart_enhanced_parity[id] = parity; // configure USART peripheral if (8 == databits && USART_ENHANCED_PARITY_NONE != parity) { // the parity bit is additional to the data bits usart_set_databits(usart, 9); } else { usart_set_databits(usart, 8); } usart_set_parity(usart, USART_PARITY_NONE); // set no parity since we will take care of it ourselves // we could also lower the number of stop bits when less than 8 bits are used, for higher throughput, but this is not a good idea since most UART transceiver parse 8 bits even is less is used return true; } void usart_enhanced_send(uint32_t usart, uint8_t data) { /* sanity check */ uint8_t id = usart_enhanced_id(usart); if (id >= USART_PERIPHERALS_NB) { return; } data &= ~(0xff << usart_enhanced_databits[id]); // only keep the data bits uint16_t output = data; // put value in output buffer switch (usart_enhanced_parity[id]) { case USART_ENHANCED_PARITY_NONE: // a mark is also decoded as idle/stop case USART_ENHANCED_PARITY_MARK: output |= (1 << usart_enhanced_databits[id]); // add idle state break; case USART_ENHANCED_PARITY_EVEN: if (!usart_enhanced_even_parity_lut[data]) { output |= (1 << usart_enhanced_databits[id]); } // no need to clear a bit if the parity is even break; case USART_ENHANCED_PARITY_ODD: if (usart_enhanced_even_parity_lut[data]) { output |= (1 << usart_enhanced_databits[id]); } // no need to clear a bit if the parity is odd break; case USART_ENHANCED_PARITY_SPACE: // no need to clear the bit break; } output |= (0xffff << (usart_enhanced_databits[id] + 1)); // set additional bits to idle (high) usart_send(usart, output); // transmit character } uint8_t usart_enhanced_recv(uint32_t usart) { /* sanity check */ uint8_t id = usart_enhanced_id(usart); if (id >= USART_PERIPHERALS_NB) { return 0xff; } uint16_t input = usart_recv(usart); // read received character (also clears the error flags) uint8_t data = input & ~(0xffff << usart_enhanced_databits[id]); // only keep the data bits // check the parity uint16_t parity = input & (1 << usart_enhanced_databits[id]); // only keep the parity bit usart_enhanced_parity_errors[id] = false; switch (usart_enhanced_parity[id]) { case USART_ENHANCED_PARITY_NONE: usart_enhanced_parity_errors[id] = false; break; case USART_ENHANCED_PARITY_EVEN: if (parity) { usart_enhanced_parity_errors[id] = !usart_enhanced_even_parity_lut[data]; } else { usart_enhanced_parity_errors[id] = usart_enhanced_even_parity_lut[data]; } break; case USART_ENHANCED_PARITY_ODD: if (parity) { usart_enhanced_parity_errors[id] = usart_enhanced_even_parity_lut[data]; } else { usart_enhanced_parity_errors[id] = !usart_enhanced_even_parity_lut[data]; } break; case USART_ENHANCED_PARITY_MARK: usart_enhanced_parity_errors[id] = !parity; break; case USART_ENHANCED_PARITY_SPACE: usart_enhanced_parity_errors[id] = parity; break; } return data; } bool usart_enhanced_parity_error(uint32_t usart) { /* sanity check */ uint8_t id = usart_enhanced_id(usart); if (id >= USART_PERIPHERALS_NB) { return false; } return usart_enhanced_parity_errors[id]; }