3771a097da
The three classes I2C, SPI and USART now have a fairly uniform (Python) API. All are constructed, initialised and deinitialised in the same way. They can have most of their parameters set, using keyword arguments. All have send and recv (although slightly different with I2C requiring an address in master mode). recv can do inplace receiving (ie store the data in a previously-created bytearray). It's just polling mode at the moment, but interrupt and DMA would be nice to add.
403 lines
13 KiB
C
403 lines
13 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "stm32f4xx_hal.h"
|
|
|
|
#include "nlr.h"
|
|
#include "misc.h"
|
|
#include "mpconfig.h"
|
|
#include "qstr.h"
|
|
#include "obj.h"
|
|
#include "runtime.h"
|
|
#include "bufhelper.h"
|
|
#include "usart.h"
|
|
|
|
struct _pyb_usart_obj_t {
|
|
mp_obj_base_t base;
|
|
pyb_usart_t usart_id;
|
|
bool is_enabled;
|
|
UART_HandleTypeDef uart;
|
|
};
|
|
|
|
pyb_usart_obj_t *pyb_usart_global_debug = NULL;
|
|
|
|
// assumes Init parameters have been set up correctly
|
|
bool usart_init2(pyb_usart_obj_t *usart_obj) {
|
|
USART_TypeDef *USARTx = NULL;
|
|
|
|
uint32_t GPIO_Pin = 0;
|
|
uint8_t GPIO_AF_USARTx = 0;
|
|
GPIO_TypeDef* GPIO_Port = NULL;
|
|
|
|
switch (usart_obj->usart_id) {
|
|
// USART1 is on PA9/PA10, PB6/PB7
|
|
case PYB_USART_1:
|
|
USARTx = USART1;
|
|
GPIO_AF_USARTx = GPIO_AF7_USART1;
|
|
|
|
#if defined(PYBV10)
|
|
GPIO_Port = GPIOB;
|
|
GPIO_Pin = GPIO_PIN_6 | GPIO_PIN_7;
|
|
#else
|
|
GPIO_Port = GPIOA;
|
|
GPIO_Pin = GPIO_PIN_9 | GPIO_PIN_10;
|
|
#endif
|
|
|
|
__USART1_CLK_ENABLE();
|
|
break;
|
|
|
|
#if !defined(PYBV10)
|
|
// USART2 is on PA2/PA3, PD5/PD6
|
|
case PYB_USART_2:
|
|
USARTx = USART2;
|
|
GPIO_AF_USARTx = GPIO_AF7_USART2;
|
|
|
|
GPIO_Port = GPIOD;
|
|
GPIO_Pin = GPIO_PIN_5 | GPIO_PIN_6;
|
|
|
|
__USART2_CLK_ENABLE();
|
|
break;
|
|
#endif
|
|
|
|
// USART3 is on PB10/PB11, PC10/PC11, PD8/PD9
|
|
case PYB_USART_3:
|
|
USARTx = USART3;
|
|
GPIO_AF_USARTx = GPIO_AF7_USART3;
|
|
|
|
#if defined(PYBV3) || defined(PYBV4) | defined(PYBV10)
|
|
GPIO_Port = GPIOB;
|
|
GPIO_Pin = GPIO_PIN_10 | GPIO_PIN_11;
|
|
#else
|
|
GPIO_Port = GPIOD;
|
|
GPIO_Pin = GPIO_PIN_8 | GPIO_PIN_9;
|
|
#endif
|
|
__USART3_CLK_ENABLE();
|
|
break;
|
|
|
|
// UART4 is on PA0/PA1, PC10/PC11
|
|
case PYB_USART_4:
|
|
USARTx = UART4;
|
|
GPIO_AF_USARTx = GPIO_AF8_UART4;
|
|
|
|
GPIO_Port = GPIOA;
|
|
GPIO_Pin = GPIO_PIN_0 | GPIO_PIN_1;
|
|
|
|
__UART4_CLK_ENABLE();
|
|
break;
|
|
|
|
// USART6 is on PC6/PC7
|
|
case PYB_USART_6:
|
|
USARTx = USART6;
|
|
GPIO_AF_USARTx = GPIO_AF8_USART6;
|
|
|
|
GPIO_Port = GPIOC;
|
|
GPIO_Pin = GPIO_PIN_6 | GPIO_PIN_7;
|
|
|
|
__USART6_CLK_ENABLE();
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// init GPIO
|
|
GPIO_InitTypeDef GPIO_InitStructure;
|
|
GPIO_InitStructure.Pin = GPIO_Pin;
|
|
GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
|
|
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
|
|
GPIO_InitStructure.Pull = GPIO_PULLUP;
|
|
GPIO_InitStructure.Alternate = GPIO_AF_USARTx;
|
|
HAL_GPIO_Init(GPIO_Port, &GPIO_InitStructure);
|
|
|
|
// init USARTx
|
|
usart_obj->uart.Instance = USARTx;
|
|
HAL_UART_Init(&usart_obj->uart);
|
|
|
|
usart_obj->is_enabled = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool usart_init(pyb_usart_obj_t *usart_obj, uint32_t baudrate) {
|
|
UART_HandleTypeDef *uh = &usart_obj->uart;
|
|
memset(uh, 0, sizeof(*uh));
|
|
uh->Init.BaudRate = baudrate;
|
|
uh->Init.WordLength = UART_WORDLENGTH_8B;
|
|
uh->Init.StopBits = UART_STOPBITS_1;
|
|
uh->Init.Parity = UART_PARITY_NONE;
|
|
uh->Init.Mode = UART_MODE_TX_RX;
|
|
uh->Init.HwFlowCtl = UART_HWCONTROL_NONE;
|
|
uh->Init.OverSampling = UART_OVERSAMPLING_16;
|
|
return usart_init2(usart_obj);
|
|
}
|
|
|
|
void usart_deinit(pyb_usart_obj_t *usart_obj) {
|
|
usart_obj->is_enabled = false;
|
|
UART_HandleTypeDef *uart = &usart_obj->uart;
|
|
HAL_UART_DeInit(uart);
|
|
if (uart->Instance == USART1) {
|
|
__USART1_FORCE_RESET();
|
|
__USART1_RELEASE_RESET();
|
|
__USART1_CLK_DISABLE();
|
|
} else if (uart->Instance == USART2) {
|
|
__USART2_FORCE_RESET();
|
|
__USART2_RELEASE_RESET();
|
|
__USART2_CLK_DISABLE();
|
|
} else if (uart->Instance == USART3) {
|
|
__USART3_FORCE_RESET();
|
|
__USART3_RELEASE_RESET();
|
|
__USART3_CLK_DISABLE();
|
|
} else if (uart->Instance == UART4) {
|
|
__UART4_FORCE_RESET();
|
|
__UART4_RELEASE_RESET();
|
|
__UART4_CLK_DISABLE();
|
|
} else if (uart->Instance == USART6) {
|
|
__USART6_FORCE_RESET();
|
|
__USART6_RELEASE_RESET();
|
|
__USART6_CLK_DISABLE();
|
|
}
|
|
}
|
|
|
|
bool usart_rx_any(pyb_usart_obj_t *usart_obj) {
|
|
return __HAL_UART_GET_FLAG(&usart_obj->uart, UART_FLAG_RXNE);
|
|
}
|
|
|
|
int usart_rx_char(pyb_usart_obj_t *usart_obj) {
|
|
uint8_t ch;
|
|
if (HAL_UART_Receive(&usart_obj->uart, &ch, 1, 0) != HAL_OK) {
|
|
ch = 0;
|
|
}
|
|
return ch;
|
|
}
|
|
|
|
void usart_tx_char(pyb_usart_obj_t *usart_obj, int c) {
|
|
uint8_t ch = c;
|
|
HAL_UART_Transmit(&usart_obj->uart, &ch, 1, 100000);
|
|
}
|
|
|
|
void usart_tx_str(pyb_usart_obj_t *usart_obj, const char *str) {
|
|
HAL_UART_Transmit(&usart_obj->uart, (uint8_t*)str, strlen(str), 100000);
|
|
}
|
|
|
|
void usart_tx_strn(pyb_usart_obj_t *usart_obj, const char *str, uint len) {
|
|
HAL_UART_Transmit(&usart_obj->uart, (uint8_t*)str, len, 100000);
|
|
}
|
|
|
|
void usart_tx_strn_cooked(pyb_usart_obj_t *usart_obj, const char *str, uint len) {
|
|
for (const char *top = str + len; str < top; str++) {
|
|
if (*str == '\n') {
|
|
usart_tx_char(usart_obj, '\r');
|
|
}
|
|
usart_tx_char(usart_obj, *str);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* Micro Python bindings */
|
|
|
|
STATIC void pyb_usart_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
|
|
pyb_usart_obj_t *self = self_in;
|
|
if (!self->is_enabled) {
|
|
print(env, "USART(%lu)", self->usart_id);
|
|
} else {
|
|
print(env, "USART(%lu, baudrate=%u, bits=%u, stop=%u",
|
|
self->usart_id, self->uart.Init.BaudRate,
|
|
self->uart.Init.WordLength == UART_WORDLENGTH_8B ? 8 : 9,
|
|
self->uart.Init.StopBits == UART_STOPBITS_1 ? 1 : 2);
|
|
if (self->uart.Init.Parity == UART_PARITY_NONE) {
|
|
print(env, ", parity=None)");
|
|
} else {
|
|
print(env, ", parity=%u)", self->uart.Init.Parity == UART_PARITY_EVEN ? 0 : 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
STATIC const mp_arg_parse_t pyb_usart_init_accepted_args[] = {
|
|
{ MP_QSTR_baudrate, MP_ARG_PARSE_REQUIRED | MP_ARG_PARSE_INT, {.u_int = 9600} },
|
|
{ MP_QSTR_bits, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 8} },
|
|
{ MP_QSTR_stop, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 1} },
|
|
{ MP_QSTR_parity, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_OBJ, {.u_obj = mp_const_none} },
|
|
};
|
|
#define PYB_USART_INIT_NUM_ARGS (sizeof(pyb_usart_init_accepted_args) / sizeof(pyb_usart_init_accepted_args[0]))
|
|
|
|
STATIC mp_obj_t pyb_usart_init_helper(pyb_usart_obj_t *self, uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
|
// parse args
|
|
mp_arg_parse_val_t vals[PYB_USART_INIT_NUM_ARGS];
|
|
mp_arg_parse_all(n_args, args, kw_args, PYB_USART_INIT_NUM_ARGS, pyb_usart_init_accepted_args, vals);
|
|
|
|
// set the USART configuration values
|
|
memset(&self->uart, 0, sizeof(self->uart));
|
|
UART_InitTypeDef *init = &self->uart.Init;
|
|
init->BaudRate = vals[0].u_int;
|
|
init->WordLength = vals[1].u_int == 8 ? UART_WORDLENGTH_8B : UART_WORDLENGTH_9B;
|
|
switch (vals[2].u_int) {
|
|
case 1: init->StopBits = UART_STOPBITS_1; break;
|
|
default: init->StopBits = UART_STOPBITS_2; break;
|
|
}
|
|
if (vals[3].u_obj == mp_const_none) {
|
|
init->Parity = UART_PARITY_NONE;
|
|
} else {
|
|
machine_int_t parity = mp_obj_get_int(vals[3].u_obj);
|
|
init->Parity = (parity & 1) ? UART_PARITY_ODD : UART_PARITY_EVEN;
|
|
}
|
|
init->Mode = UART_MODE_TX_RX;
|
|
init->HwFlowCtl = UART_HWCONTROL_NONE;
|
|
init->OverSampling = UART_OVERSAMPLING_16;
|
|
|
|
// init USART (if it fails, it's because the port doesn't exist)
|
|
if (!usart_init2(self)) {
|
|
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "USART port %d does not exist", self->usart_id));
|
|
}
|
|
|
|
return mp_const_none;
|
|
}
|
|
|
|
STATIC mp_obj_t pyb_usart_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
|
|
// check arguments
|
|
mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true);
|
|
|
|
// create object
|
|
pyb_usart_obj_t *o = m_new_obj(pyb_usart_obj_t);
|
|
o->base.type = &pyb_usart_type;
|
|
|
|
// work out port
|
|
o->usart_id = 0;
|
|
if (MP_OBJ_IS_STR(args[0])) {
|
|
const char *port = mp_obj_str_get_str(args[0]);
|
|
if (0) {
|
|
#if defined(PYBV10)
|
|
} else if (strcmp(port, "XA") == 0) {
|
|
o->usart_id = PYB_USART_XA;
|
|
} else if (strcmp(port, "XB") == 0) {
|
|
o->usart_id = PYB_USART_XB;
|
|
} else if (strcmp(port, "YA") == 0) {
|
|
o->usart_id = PYB_USART_YA;
|
|
} else if (strcmp(port, "YB") == 0) {
|
|
o->usart_id = PYB_USART_YB;
|
|
#endif
|
|
} else {
|
|
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "USART port %s does not exist", port));
|
|
}
|
|
} else {
|
|
o->usart_id = mp_obj_get_int(args[0]);
|
|
}
|
|
|
|
if (n_args > 1 || n_kw > 0) {
|
|
// start the peripheral
|
|
mp_map_t kw_args;
|
|
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
|
|
pyb_usart_init_helper(o, n_args - 1, args + 1, &kw_args);
|
|
}
|
|
|
|
return o;
|
|
}
|
|
|
|
STATIC mp_obj_t pyb_usart_init(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
|
return pyb_usart_init_helper(args[0], n_args - 1, args + 1, kw_args);
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usart_init_obj, 1, pyb_usart_init);
|
|
|
|
STATIC mp_obj_t pyb_usart_deinit(mp_obj_t self_in) {
|
|
pyb_usart_obj_t *self = self_in;
|
|
usart_deinit(self);
|
|
return mp_const_none;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_usart_deinit_obj, pyb_usart_deinit);
|
|
|
|
STATIC mp_obj_t pyb_usart_any(mp_obj_t self_in) {
|
|
pyb_usart_obj_t *self = self_in;
|
|
if (usart_rx_any(self)) {
|
|
return mp_const_true;
|
|
} else {
|
|
return mp_const_false;
|
|
}
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_usart_any_obj, pyb_usart_any);
|
|
|
|
STATIC const mp_arg_parse_t pyb_usart_send_accepted_args[] = {
|
|
{ MP_QSTR_send, MP_ARG_PARSE_REQUIRED | MP_ARG_PARSE_OBJ, {.u_obj = MP_OBJ_NULL} },
|
|
{ MP_QSTR_timeout, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 5000} },
|
|
};
|
|
#define PYB_USART_SEND_NUM_ARGS (sizeof(pyb_usart_send_accepted_args) / sizeof(pyb_usart_send_accepted_args[0]))
|
|
|
|
STATIC mp_obj_t pyb_usart_send(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
|
// TODO assumes transmission size is 8-bits wide
|
|
|
|
pyb_usart_obj_t *self = args[0];
|
|
|
|
// parse args
|
|
mp_arg_parse_val_t vals[PYB_USART_SEND_NUM_ARGS];
|
|
mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_USART_SEND_NUM_ARGS, pyb_usart_send_accepted_args, vals);
|
|
|
|
// get the buffer to send from
|
|
mp_buffer_info_t bufinfo;
|
|
uint8_t data[1];
|
|
pyb_buf_get_for_send(vals[0].u_obj, &bufinfo, data);
|
|
|
|
// send the data
|
|
HAL_StatusTypeDef status = HAL_UART_Transmit(&self->uart, bufinfo.buf, bufinfo.len, vals[1].u_int);
|
|
|
|
if (status != HAL_OK) {
|
|
// TODO really need a HardwareError object, or something
|
|
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_UART_Transmit failed with code %d", status));
|
|
}
|
|
|
|
return mp_const_none;
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usart_send_obj, 1, pyb_usart_send);
|
|
|
|
STATIC const mp_arg_parse_t pyb_usart_recv_accepted_args[] = {
|
|
{ MP_QSTR_recv, MP_ARG_PARSE_REQUIRED | MP_ARG_PARSE_OBJ, {.u_obj = MP_OBJ_NULL} },
|
|
{ MP_QSTR_timeout, MP_ARG_PARSE_KW_ONLY | MP_ARG_PARSE_INT, {.u_int = 5000} },
|
|
};
|
|
#define PYB_USART_RECV_NUM_ARGS (sizeof(pyb_usart_recv_accepted_args) / sizeof(pyb_usart_recv_accepted_args[0]))
|
|
|
|
STATIC mp_obj_t pyb_usart_recv(uint n_args, const mp_obj_t *args, mp_map_t *kw_args) {
|
|
// TODO assumes transmission size is 8-bits wide
|
|
|
|
pyb_usart_obj_t *self = args[0];
|
|
|
|
// parse args
|
|
mp_arg_parse_val_t vals[PYB_USART_RECV_NUM_ARGS];
|
|
mp_arg_parse_all(n_args - 1, args + 1, kw_args, PYB_USART_RECV_NUM_ARGS, pyb_usart_recv_accepted_args, vals);
|
|
|
|
// get the buffer to receive into
|
|
mp_buffer_info_t bufinfo;
|
|
mp_obj_t o_ret = pyb_buf_get_for_recv(vals[0].u_obj, &bufinfo);
|
|
|
|
// receive the data
|
|
HAL_StatusTypeDef status = HAL_UART_Receive(&self->uart, bufinfo.buf, bufinfo.len, vals[1].u_int);
|
|
|
|
if (status != HAL_OK) {
|
|
// TODO really need a HardwareError object, or something
|
|
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_Exception, "HAL_UART_Receive failed with code %d", status));
|
|
}
|
|
|
|
// return the received data
|
|
if (o_ret == MP_OBJ_NULL) {
|
|
return vals[0].u_obj;
|
|
} else {
|
|
return mp_obj_str_builder_end(o_ret);
|
|
}
|
|
}
|
|
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pyb_usart_recv_obj, 1, pyb_usart_recv);
|
|
|
|
STATIC const mp_map_elem_t pyb_usart_locals_dict_table[] = {
|
|
// instance methods
|
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&pyb_usart_init_obj },
|
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_deinit), (mp_obj_t)&pyb_usart_deinit_obj },
|
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&pyb_usart_any_obj },
|
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_usart_send_obj },
|
|
{ MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_usart_recv_obj },
|
|
};
|
|
|
|
STATIC MP_DEFINE_CONST_DICT(pyb_usart_locals_dict, pyb_usart_locals_dict_table);
|
|
|
|
const mp_obj_type_t pyb_usart_type = {
|
|
{ &mp_type_type },
|
|
.name = MP_QSTR_USART,
|
|
.print = pyb_usart_print,
|
|
.make_new = pyb_usart_make_new,
|
|
.locals_dict = (mp_obj_t)&pyb_usart_locals_dict,
|
|
};
|