circuitpython/ports/stm/common-hal/busio/UART.c
Lucian Copeland c4db8b87e2 Add F7 and H7 Support to the STM32 port
Restructures the STM port of Circuitpython to be more generic about the STM32 chip lines to support
the F7 and H7 series of chips. Adds the new Packages directory to organize different chip layouts
between lines. Makes general changes to the Makefile to condense board-level flags to the minimum
and support the new chip series. Adds the new chip line to the Peripherals directory, along with
new python tools used to generate peripheral text automatically in the tools/ directory.
2020-03-26 18:01:17 -04:00

626 lines
19 KiB
C

/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2019 Lucian Copeland for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/busio/UART.h"
#include "mpconfigport.h"
#include "lib/utils/interrupt_char.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "supervisor/shared/translate.h"
#include "tick.h"
#define ALL_UARTS 0xFFFF
//arrays use 0 based numbering: UART1 is stored at index 0
STATIC bool reserved_uart[MAX_UART];
int errflag; //Used to restart read halts
STATIC void uart_clock_enable(uint16_t mask);
STATIC void uart_clock_disable(uint16_t mask);
STATIC void uart_assign_irq(busio_uart_obj_t* self, USART_TypeDef* USARTx);
STATIC USART_TypeDef * assign_uart_or_throw(busio_uart_obj_t* self, bool pin_eval,
int periph_index, bool uart_taken) {
if (pin_eval) {
//assign a root pointer pointer for IRQ
MP_STATE_PORT(cpy_uart_obj_all)[periph_index] = self;
return mcu_uart_banks[periph_index];
} else {
if (uart_taken) {
mp_raise_ValueError(translate("Hardware in use, try alternative pins"));
} else {
mp_raise_ValueError(translate("Invalid UART pin selection"));
}
}
}
void uart_reset(void) {
for (uint8_t i = 0; i < MAX_UART; i++) {
reserved_uart[i] = false;
MP_STATE_PORT(cpy_uart_obj_all)[i] = NULL;
}
uart_clock_disable(ALL_UARTS);
}
void common_hal_busio_uart_construct(busio_uart_obj_t *self,
const mcu_pin_obj_t * tx, const mcu_pin_obj_t * rx,
const mcu_pin_obj_t * rts, const mcu_pin_obj_t * cts,
const mcu_pin_obj_t * rs485_dir, bool rs485_invert,
uint32_t baudrate, uint8_t bits, uart_parity_t parity, uint8_t stop,
mp_float_t timeout, uint16_t receiver_buffer_size) {
//match pins to UART objects
USART_TypeDef * USARTx;
uint8_t tx_len = MP_ARRAY_SIZE(mcu_uart_tx_list);
uint8_t rx_len = MP_ARRAY_SIZE(mcu_uart_rx_list);
bool uart_taken = false;
uint8_t periph_index = 0; //origin 0 corrected
if ((rts != NULL) || (cts != NULL) || (rs485_dir != NULL) || (rs485_invert == true)) {
mp_raise_ValueError(translate("RTS/CTS/RS485 Not yet supported on this device"));
}
//Can have both pins, or either
if ((tx != NULL) && (rx != NULL)) {
//normal find loop if both pins exist
for (uint i = 0; i < tx_len; i++) {
if (mcu_uart_tx_list[i].pin == tx) {
//rx
for (uint j = 0; j < rx_len; j++) {
if (mcu_uart_rx_list[j].pin == rx
&& mcu_uart_rx_list[j].periph_index == mcu_uart_tx_list[i].periph_index) {
//keep looking if the UART is taken, edge case
if (reserved_uart[mcu_uart_tx_list[i].periph_index - 1]) {
uart_taken = true;
continue;
}
//store pins if not
self->tx = &mcu_uart_tx_list[i];
self->rx = &mcu_uart_rx_list[j];
break;
}
}
}
}
periph_index = self->tx->periph_index - 1;
USARTx = assign_uart_or_throw(self, (self->tx != NULL && self->rx != NULL),
periph_index, uart_taken);
} else if (tx == NULL) {
//If there is no tx, run only rx
for (uint i = 0; i < rx_len; i++) {
if (mcu_uart_rx_list[i].pin == rx) {
//keep looking if the UART is taken, edge case
if (reserved_uart[mcu_uart_rx_list[i].periph_index - 1]) {
uart_taken = true;
continue;
}
//store pins if not
self->rx = &mcu_uart_rx_list[i];
break;
}
}
periph_index = self->rx->periph_index - 1;
USARTx = assign_uart_or_throw(self, (self->rx != NULL),
periph_index, uart_taken);
} else if (rx == NULL) {
//If there is no rx, run only tx
for (uint i = 0; i < tx_len; i++) {
if (mcu_uart_tx_list[i].pin == tx) {
//keep looking if the UART is taken, edge case
if (reserved_uart[mcu_uart_tx_list[i].periph_index - 1]) {
uart_taken = true;
continue;
}
//store pins if not
self->tx = &mcu_uart_tx_list[i];
break;
}
}
periph_index = self->tx->periph_index - 1;
USARTx = assign_uart_or_throw(self, (self->tx != NULL),
periph_index, uart_taken);
} else {
//both pins cannot be empty
mp_raise_ValueError(translate("Supply at least one UART pin"));
}
//Other errors
if ( receiver_buffer_size == 0 ) {
mp_raise_ValueError(translate("Invalid buffer size"));
}
if ( bits != 8 && bits != 9 ) {
mp_raise_ValueError(translate("Invalid word/bit length"));
}
if ( USARTx == NULL) { //this can only be hit if the periph file is wrong
mp_raise_ValueError(translate("Internal define error"));
}
//GPIO Init
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (self->tx != NULL) {
GPIO_InitStruct.Pin = pin_mask(tx->number);
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = self->tx->altfn_index;
HAL_GPIO_Init(pin_port(tx->port), &GPIO_InitStruct);
}
if (self->rx != NULL) {
GPIO_InitStruct.Pin = pin_mask(rx->number);
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = self->rx->altfn_index;
HAL_GPIO_Init(pin_port(rx->port), &GPIO_InitStruct);
}
//reserve uart and enable the peripheral
reserved_uart[periph_index] = true;
uart_clock_enable(1 << (periph_index));
uart_assign_irq(self, USARTx);
self->handle.Instance = USARTx;
self->handle.Init.BaudRate = baudrate;
self->handle.Init.WordLength = (bits == 9) ? UART_WORDLENGTH_9B : UART_WORDLENGTH_8B;
self->handle.Init.StopBits = (stop > 1) ? UART_STOPBITS_2 : UART_STOPBITS_1;
self->handle.Init.Parity = (parity == PARITY_ODD) ? UART_PARITY_ODD :
(parity == PARITY_EVEN) ? UART_PARITY_EVEN :
UART_PARITY_NONE;
self->handle.Init.Mode = (self->tx != NULL && self->rx != NULL) ? UART_MODE_TX_RX :
(self->tx != NULL) ? UART_MODE_TX :
UART_MODE_RX;
self->handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
self->handle.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&self->handle) != HAL_OK)
{
mp_raise_ValueError(translate("UART Init Error"));
}
// Init buffer for rx and claim pins
if (self->rx != NULL) {
ringbuf_alloc(&self->rbuf, receiver_buffer_size, true);
if (!self->rbuf.buf) {
mp_raise_ValueError(translate("UART Buffer allocation error"));
}
claim_pin(rx);
}
if (self->tx != NULL) {
claim_pin(tx);
}
self->baudrate = baudrate;
self->timeout_ms = timeout * 1000;
//start the interrupt series
if ((HAL_UART_GetState(&self->handle) & HAL_UART_STATE_BUSY_RX) == HAL_UART_STATE_BUSY_RX) {
mp_raise_ValueError(translate("Could not start interrupt, RX busy"));
}
//start the receive interrupt chain
HAL_NVIC_DisableIRQ(self->irq); //prevent handle lock contention
HAL_UART_Receive_IT(&self->handle, &self->rx_char, 1);
HAL_NVIC_SetPriority(self->irq, UART_IRQPRI, UART_IRQSUB_PRI);
HAL_NVIC_EnableIRQ(self->irq);
errflag = HAL_OK;
}
bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) {
return self->tx->pin == NULL;
}
void common_hal_busio_uart_deinit(busio_uart_obj_t *self) {
if (common_hal_busio_uart_deinited(self)) return;
reset_pin_number(self->tx->pin->port,self->tx->pin->number);
reset_pin_number(self->rx->pin->port,self->rx->pin->number);
self->tx = NULL;
self->rx = NULL;
gc_free(self->rbuf.buf);
self->rbuf.size = 0;
self->rbuf.iput = self->rbuf.iget = 0;
}
size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) {
if (self->rx == NULL) {
mp_raise_ValueError(translate("No RX pin"));
}
size_t rx_bytes = 0;
uint64_t start_ticks = supervisor_ticks_ms64();
// Wait for all bytes received or timeout, same as nrf
while ( (ringbuf_count(&self->rbuf) < len) && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms) ) {
RUN_BACKGROUND_TASKS;
//restart if it failed in the callback
if (errflag != HAL_OK) {
errflag = HAL_UART_Receive_IT(&self->handle, &self->rx_char, 1);
}
// Allow user to break out of a timeout with a KeyboardInterrupt.
if ( mp_hal_is_interrupted() ) {
return 0;
}
}
// Halt reception
HAL_NVIC_DisableIRQ(self->irq);
// copy received data
rx_bytes = ringbuf_count(&self->rbuf);
rx_bytes = MIN(rx_bytes, len);
for (uint16_t i = 0; i < rx_bytes; i++) {
data[i] = ringbuf_get(&self->rbuf);
}
HAL_NVIC_EnableIRQ(self->irq);
if (rx_bytes == 0) {
*errcode = EAGAIN;
return MP_STREAM_ERROR;
}
return rx_bytes;
}
// Write characters.
size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) {
if (self->tx == NULL) {
mp_raise_ValueError(translate("No TX pin"));
}
bool write_err = false; //write error shouldn't disable interrupts
HAL_NVIC_DisableIRQ(self->irq);
if (HAL_UART_Transmit(&self->handle, (uint8_t*)data, len, HAL_MAX_DELAY) != HAL_OK) {
write_err = true;
}
HAL_UART_Receive_IT(&self->handle, &self->rx_char, 1);
HAL_NVIC_EnableIRQ(self->irq);
if (write_err) {
mp_raise_ValueError(translate("UART write error"));
}
return len;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *handle)
{
for (int i = 0; i < 7; i++) {
//get context pointer and cast it as struct pointer
busio_uart_obj_t * context = (busio_uart_obj_t*)MP_STATE_PORT(cpy_uart_obj_all)[i];
if (handle == &context->handle) {
//check if transaction is ongoing
if ((HAL_UART_GetState(handle) & HAL_UART_STATE_BUSY_RX) == HAL_UART_STATE_BUSY_RX) {
return;
}
ringbuf_put_n(&context->rbuf, &context->rx_char, 1);
errflag = HAL_UART_Receive_IT(handle, &context->rx_char, 1);
return;
}
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle)
{
if (__HAL_UART_GET_FLAG(UartHandle, UART_FLAG_PE) != RESET) {
__HAL_UART_CLEAR_PEFLAG(UartHandle);
} else if (__HAL_UART_GET_FLAG(UartHandle, UART_FLAG_FE) != RESET) {
__HAL_UART_CLEAR_FEFLAG(UartHandle);
} else if (__HAL_UART_GET_FLAG(UartHandle, UART_FLAG_NE) != RESET) {
__HAL_UART_CLEAR_NEFLAG(UartHandle);
} else if (__HAL_UART_GET_FLAG(UartHandle, UART_FLAG_ORE) != RESET) {
__HAL_UART_CLEAR_OREFLAG(UartHandle);
}
//restart serial read after an error
for (int i = 0; i < 7; i++) {
busio_uart_obj_t * context = (busio_uart_obj_t *)MP_STATE_PORT(cpy_uart_obj_all)[i];
if (UartHandle == &context->handle) {
HAL_UART_Receive_IT(UartHandle, &context->rx_char, 1);
return;
}
}
}
uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) {
return self->baudrate;
}
void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) {
//Don't reset if it's the same value
if (baudrate == self->baudrate) return;
//Otherwise de-init and set new rate
if (HAL_UART_DeInit(&self->handle) != HAL_OK) {
mp_raise_ValueError(translate("UART De-init error"));
}
self->handle.Init.BaudRate = baudrate;
if (HAL_UART_Init(&self->handle) != HAL_OK) {
mp_raise_ValueError(translate("UART Re-init error"));
}
self->baudrate = baudrate;
}
mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) {
return (mp_float_t) (self->timeout_ms / 1000.0f);
}
void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) {
self->timeout_ms = timeout * 1000;
}
uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) {
return ringbuf_count(&self->rbuf);
}
void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) {
// Halt reception
HAL_NVIC_DisableIRQ(self->irq);
ringbuf_clear(&self->rbuf);
HAL_NVIC_EnableIRQ(self->irq);
}
bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) {
return __HAL_UART_GET_FLAG(&self->handle, UART_FLAG_TXE);
}
STATIC void call_hal_irq(int uart_num) {
//Create casted context pointer
busio_uart_obj_t * context = (busio_uart_obj_t*)MP_STATE_PORT(cpy_uart_obj_all)[uart_num - 1];
if (context != NULL) {
HAL_NVIC_ClearPendingIRQ(context->irq);
HAL_UART_IRQHandler(&context->handle);
}
}
// UART/USART IRQ handlers
void USART1_IRQHandler(void) {
call_hal_irq(1);
}
void USART2_IRQHandler(void) {
call_hal_irq(2);
}
void USART3_IRQHandler(void) {
call_hal_irq(3);
}
void UART4_IRQHandler(void) {
call_hal_irq(4);
}
void UART5_IRQHandler(void) {
call_hal_irq(5);
}
void USART6_IRQHandler(void) {
call_hal_irq(6);
}
STATIC void uart_clock_enable(uint16_t mask) {
#ifdef USART1
if (mask & (1 << 0)) {
__HAL_RCC_USART1_FORCE_RESET();
__HAL_RCC_USART1_RELEASE_RESET();
__HAL_RCC_USART1_CLK_ENABLE();
}
#endif
#ifdef USART2
if (mask & (1 << 1)) {
__HAL_RCC_USART2_FORCE_RESET();
__HAL_RCC_USART2_RELEASE_RESET();
__HAL_RCC_USART2_CLK_ENABLE();
}
#endif
#ifdef USART3
if (mask & (1 << 2)) {
__HAL_RCC_USART3_FORCE_RESET();
__HAL_RCC_USART3_RELEASE_RESET();
__HAL_RCC_USART3_CLK_ENABLE();
}
#endif
#ifdef UART4
if (mask & (1 << 3)) {
__HAL_RCC_UART4_FORCE_RESET();
__HAL_RCC_UART4_RELEASE_RESET();
__HAL_RCC_UART4_CLK_ENABLE();
}
#endif
#ifdef UART5
if (mask & (1 << 4)) {
__HAL_RCC_UART5_FORCE_RESET();
__HAL_RCC_UART5_RELEASE_RESET();
__HAL_RCC_UART5_CLK_ENABLE();
}
#endif
#ifdef USART6
if (mask & (1 << 5)) {
__HAL_RCC_USART6_FORCE_RESET();
__HAL_RCC_USART6_RELEASE_RESET();
__HAL_RCC_USART6_CLK_ENABLE();
}
#endif
#ifdef UART7
if (mask & (1 << 6)) {
__HAL_RCC_UART7_FORCE_RESET();
__HAL_RCC_UART7_RELEASE_RESET();
__HAL_RCC_UART7_CLK_ENABLE();
}
#endif
#ifdef UART8
if (mask & (1 << 7)) {
__HAL_RCC_UART8_FORCE_RESET();
__HAL_RCC_UART8_RELEASE_RESET();
__HAL_RCC_UART8_CLK_ENABLE();
}
#endif
#ifdef UART9
if (mask & (1 << 8)) {
__HAL_RCC_UART9_FORCE_RESET();
__HAL_RCC_UART9_RELEASE_RESET();
__HAL_RCC_UART9_CLK_ENABLE();
}
#endif
#ifdef UART10
if (mask & (1 << 9)) {
__HAL_RCC_UART10_FORCE_RESET();
__HAL_RCC_UART10_RELEASE_RESET();
__HAL_RCC_UART10_CLK_ENABLE();
}
#endif
}
STATIC void uart_clock_disable(uint16_t mask) {
#ifdef USART1
if (mask & (1 << 0)) {
__HAL_RCC_USART1_FORCE_RESET();
__HAL_RCC_USART1_RELEASE_RESET();
__HAL_RCC_USART1_CLK_DISABLE();
}
#endif
#ifdef USART2
if (mask & (1 << 1)) {
__HAL_RCC_USART2_FORCE_RESET();
__HAL_RCC_USART2_RELEASE_RESET();
__HAL_RCC_USART2_CLK_DISABLE();
}
#endif
#ifdef USART3
if (mask & (1 << 2)) {
__HAL_RCC_USART3_FORCE_RESET();
__HAL_RCC_USART3_RELEASE_RESET();
__HAL_RCC_USART3_CLK_DISABLE();
}
#endif
#ifdef UART4
if (mask & (1 << 3)) {
__HAL_RCC_UART4_FORCE_RESET();
__HAL_RCC_UART4_RELEASE_RESET();
__HAL_RCC_UART4_CLK_DISABLE();
}
#endif
#ifdef UART5
if (mask & (1 << 4)) {
__HAL_RCC_UART5_FORCE_RESET();
__HAL_RCC_UART5_RELEASE_RESET();
__HAL_RCC_UART5_CLK_DISABLE();
}
#endif
#ifdef USART6
if (mask & (1 << 5)) {
__HAL_RCC_USART6_FORCE_RESET();
__HAL_RCC_USART6_RELEASE_RESET();
__HAL_RCC_USART6_CLK_DISABLE();
}
#endif
#ifdef UART7
if (mask & (1 << 6)) {
__HAL_RCC_UART7_FORCE_RESET();
__HAL_RCC_UART7_RELEASE_RESET();
__HAL_RCC_UART7_CLK_DISABLE();
}
#endif
#ifdef UART8
if (mask & (1 << 7)) {
__HAL_RCC_UART8_FORCE_RESET();
__HAL_RCC_UART8_RELEASE_RESET();
__HAL_RCC_UART8_CLK_DISABLE();
}
#endif
#ifdef UART9
if (mask & (1 << 8)) {
__HAL_RCC_UART9_FORCE_RESET();
__HAL_RCC_UART9_RELEASE_RESET();
__HAL_RCC_UART9_CLK_DISABLE();
}
#endif
#ifdef UART10
if (mask & (1 << 9)) {
__HAL_RCC_UART10_FORCE_RESET();
__HAL_RCC_UART10_RELEASE_RESET();
__HAL_RCC_UART10_CLK_DISABLE();
}
#endif
}
STATIC void uart_assign_irq(busio_uart_obj_t *self, USART_TypeDef * USARTx) {
#ifdef USART1
if (USARTx == USART1) {
self->irq = USART1_IRQn;
}
#endif
#ifdef USART2
if (USARTx == USART2) {
self->irq = USART2_IRQn;
}
#endif
#ifdef USART3
if (USARTx == USART3) {
self->irq = USART3_IRQn;
}
#endif
#ifdef UART4
if (USARTx == UART4) {
self->irq = UART4_IRQn;
}
#endif
#ifdef UART5
if (USARTx == UART5) {
self->irq = UART5_IRQn;
}
#endif
#ifdef USART6
if (USARTx == USART6) {
self->irq = USART6_IRQn;
}
#endif
#ifdef UART7
if (USARTx == UART7) {
self->irq = UART7_IRQn;
}
#endif
#ifdef UART8
if (USARTx == UART8) {
self->irq = UART8_IRQn;
}
#endif
#ifdef UART9
if (USARTx == UART9) {
self->irq = UART9_IRQn;
}
#endif
#ifdef UART10
if (USARTx == UART10) {
self->irq = UART10_IRQn;
}
#endif
}