Modularization, stage 1

This commit is contained in:
2018-07-07 19:55:21 +02:00
parent e5679e90f8
commit a1fa0821cf
105 changed files with 11851 additions and 268 deletions
+56
View File
@@ -0,0 +1,56 @@
//
// Created by MightyPork on 2018/02/03.
//
#include "platform.h"
#include "unit_base.h"
#include "unit_usart.h"
#define UUSART_INTERNAL
#include "_usart_internal.h"
error_t UU_USART_Write(Unit *unit, const uint8_t *buffer, uint32_t len)
{
CHECK_TYPE(unit, &UNIT_USART);
struct priv *priv = unit->data;
uint32_t t_start = HAL_GetTick();
while (len > 0) {
// this should be long enough even for the slowest bitrates and 512 bytes
if (HAL_GetTick() - t_start > 5000) {
return E_HW_TIMEOUT;
}
uint16_t chunk = UUSART_DMA_TxQueue(priv, buffer, (uint16_t) len);
buffer += chunk;
len -= chunk;
// We give up control if there's another thread waiting and this isn't the last cycle
if (len > 0) {
osThreadYield();
}
}
return E_SUCCESS;
}
error_t UU_USART_WriteSync(Unit *unit, const uint8_t *buffer, uint32_t len)
{
CHECK_TYPE(unit, &UNIT_USART);
struct priv *priv = unit->data;
TRY(UU_USART_Write(unit, buffer, len));
// Now wait for the last DMA to complete
uint32_t t_start = HAL_GetTick();
while (priv->tx_dma_busy) {
if (HAL_GetTick() - t_start > 1000) {
return E_HW_TIMEOUT;
}
}
return E_SUCCESS;
}
+444
View File
@@ -0,0 +1,444 @@
//
// Created by MightyPork on 2018/01/14.
//
#include "platform.h"
#include "irq_dispatcher.h"
#include "unit_base.h"
#define UUSART_INTERNAL
#include "_usart_internal.h"
static void UUSART_DMA_RxHandler(void *arg);
static void UUSART_DMA_TxHandler(void *arg);
#if UUSART_DEBUG
#define dbg_uusart(fmt, ...) dbg(fmt, ##__VA_ARGS__)
#else
#define dbg_uusart(fmt, ...)
#endif
error_t UUSART_ClaimDMAs(Unit *unit)
{
error_t rv;
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
priv->dma = DMA1;
switch (priv->periph_num) {
/* USART1 */
case 1:
// TX
rv = rsc_claim(unit, R_DMA1_2);
if (rv == E_SUCCESS) {
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART1TX_RMP_DMA1CH2);
priv->dma_tx = DMA1_Channel2;
priv->dma_tx_chnum = 2;
} else {
// try the remap
TRY(rsc_claim(unit, R_DMA1_4));
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART1TX_RMP_DMA1CH4);
priv->dma_tx = DMA1_Channel4;
priv->dma_tx_chnum = 4;
}
// RX
rv = rsc_claim(unit, R_DMA1_3);
if (rv == E_SUCCESS) {
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART1RX_RMP_DMA1CH3);
priv->dma_rx = DMA1_Channel3;
priv->dma_rx_chnum = 3;
} else {
// try the remap
TRY(rsc_claim(unit, R_DMA1_5));
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART1RX_RMP_DMA1CH5);
priv->dma_rx = DMA1_Channel5;
priv->dma_rx_chnum = 5;
}
break;
/* USART2 */
case 2:
// RX,TX
rv = rsc_claim_range(unit, R_DMA1_4, R_DMA1_5);
if (rv == E_SUCCESS) {
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART2_RMP_DMA1CH54);
priv->dma_tx = DMA1_Channel4;
priv->dma_rx = DMA1_Channel5;
priv->dma_tx_chnum = 4;
priv->dma_rx_chnum = 5;
} else {
// try the remap
TRY(rsc_claim_range(unit, R_DMA1_6, R_DMA1_7));
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART2_RMP_DMA1CH67);
priv->dma_tx = DMA1_Channel7;
priv->dma_rx = DMA1_Channel6;
priv->dma_tx_chnum = 7;
priv->dma_rx_chnum = 6;
}
break;
/* USART3 */
case 3:
// RX,TX
rv = rsc_claim_range(unit, R_DMA1_6, R_DMA1_7);
if (rv == E_SUCCESS) {
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART3_RMP_DMA1CH67);
priv->dma_tx = DMA1_Channel7;
priv->dma_rx = DMA1_Channel6;
priv->dma_tx_chnum = 7;
priv->dma_rx_chnum = 6;
} else {
// try the remap
TRY(rsc_claim_range(unit, R_DMA1_2, R_DMA1_3));
LL_SYSCFG_SetRemapDMA_USART(LL_SYSCFG_USART3_RMP_DMA1CH32);
priv->dma_tx = DMA1_Channel2;
priv->dma_rx = DMA1_Channel3;
priv->dma_tx_chnum = 2;
priv->dma_rx_chnum = 3;
}
break;
/* USART4 */
case 4:
// RX,TX
TRY(rsc_claim_range(unit, R_DMA1_6, R_DMA1_7));
priv->dma_tx = DMA1_Channel7;
priv->dma_rx = DMA1_Channel6;
priv->dma_tx_chnum = 7;
priv->dma_rx_chnum = 6;
break;
default:
trap("Missing DMA mapping for USART%d", (int)priv->periph_num);
}
dbg_uusart("USART %d - selected DMA ch Tx(%d), Rx(%d)", priv->periph_num, priv->dma_tx_chnum, priv->dma_rx_chnum);
return E_SUCCESS;
}
error_t UUSART_SetupDMAs(Unit *unit)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
priv->rx_buffer = malloc_ck(UUSART_RXBUF_LEN);
if (NULL == priv->rx_buffer) return E_OUT_OF_MEM;
priv->tx_buffer = malloc_ck(UUSART_TXBUF_LEN);
if (NULL == priv->tx_buffer) return E_OUT_OF_MEM;
// Those must be aligned to a word boundary for the DMAs to work.
// Any well-behaved malloc impl should do this correctly.
assert_param(((uint32_t)priv->rx_buffer & 3) == 0);
assert_param(((uint32_t)priv->tx_buffer & 3) == 0);
priv->rx_buf_readpos = 0;
LL_DMA_InitTypeDef init;
// Transmit buffer
{
LL_DMA_StructInit(&init);
init.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
init.Mode = LL_DMA_MODE_NORMAL;
init.PeriphOrM2MSrcAddress = (uint32_t) &priv->periph->TDR;
init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
init.MemoryOrM2MDstAddress = (uint32_t) priv->tx_buffer;
init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
assert_param(SUCCESS == LL_DMA_Init(priv->dma, priv->dma_tx_chnum, &init));
irqd_attach(priv->dma_tx, UUSART_DMA_TxHandler, unit);
// Interrupt on transfer complete
LL_DMA_EnableIT_TC(priv->dma, priv->dma_tx_chnum);
}
// Receive buffer
{
LL_DMA_StructInit(&init);
init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
init.Mode = LL_DMA_MODE_CIRCULAR;
init.NbData = UUSART_RXBUF_LEN;
init.PeriphOrM2MSrcAddress = (uint32_t) &priv->periph->RDR;
init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
init.MemoryOrM2MDstAddress = (uint32_t) priv->rx_buffer;
init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
assert_param(SUCCESS == LL_DMA_Init(priv->dma, priv->dma_rx_chnum, &init));
irqd_attach(priv->dma_rx, UUSART_DMA_RxHandler, unit);
// Interrupt on transfer 1/2 and complete
// We will capture the first and second half and send it while the other half is being filled.
LL_DMA_EnableIT_HT(priv->dma, priv->dma_rx_chnum);
LL_DMA_EnableIT_TC(priv->dma, priv->dma_rx_chnum);
}
LL_DMA_EnableChannel(priv->dma, priv->dma_rx_chnum);
LL_DMA_EnableChannel(priv->dma, priv->dma_tx_chnum);
return E_SUCCESS;
}
/**
* Handler for the Rx DMA half or full interrupt
* @param arg - unit instance
*/
static void UUSART_DMA_RxHandler(void *arg)
{
Unit *unit = arg;
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
const uint32_t isrsnapshot = priv->dma->ISR;
if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_rx_chnum)) {
bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_rx_chnum);
bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_rx_chnum);
// Here we have to either copy it somewhere else, or notify another thread (queue?)
// that the data is ready for reading
if (ht) {
uint16_t end = (uint16_t) UUSART_RXBUF_LEN / 2;
UUSART_DMA_HandleRxFromIRQ(unit, end);
LL_DMA_ClearFlag_HT(priv->dma, priv->dma_rx_chnum);
}
if (tc) {
uint16_t end = (uint16_t) UUSART_RXBUF_LEN;
UUSART_DMA_HandleRxFromIRQ(unit, end);
LL_DMA_ClearFlag_TC(priv->dma, priv->dma_rx_chnum);
}
if (LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_rx_chnum)) {
// this shouldn't happen
dbg("USART DMA TE!");
LL_DMA_ClearFlag_TE(priv->dma, priv->dma_rx_chnum);
}
}
}
/**
* Start sending a chunk of data.
* This must be called when the DMA is completed.
*
* @param priv
*/
static void UUSART_DMA_TxStart(struct priv *priv)
{
priv->tx_dma_busy = true;
assert_param(priv->dma_tx->CNDTR == 0);
dbg_uusart("DMA_TxStart (nr %d, nw %d)", (int)priv->tx_buf_nr, (int)priv->tx_buf_nw);
uint16_t nr = priv->tx_buf_nr;
uint16_t nw = priv->tx_buf_nw;
if (nr == nw) {
dbg_uusart("remain=0,do nothing");
return;
} // do nothing if we're done
uint8_t chunk = priv->tx_buffer[nr++];
//nr += (uint16_t) (4 - (nr & 0b11));
if (chunk == 0) {
// wrap-around
chunk = priv->tx_buffer[0];
nr = 1;
assert_param(nr < nw);
}
// nr was advanced by the lpad preamble
priv->tx_buf_nr = nr;
priv->tx_buf_chunk = chunk; // will be further moved by 'chunk' bytes when dma completes
dbg_uusart("# TX: chunk start %d, len %d", (int)nr, (int)chunk);
//#if UUSART_DEBUG
// PUTS(">"); PUTSN((char *) (priv->tx_buffer + nr), chunk); PUTS("<");
// PUTNL();
//#endif
LL_DMA_DisableChannel(priv->dma, priv->dma_tx_chnum);
{
LL_DMA_ClearFlags(priv->dma, priv->dma_tx_chnum);
LL_DMA_SetMemoryAddress(priv->dma, priv->dma_tx_chnum, (uint32_t) (priv->tx_buffer + nr));
LL_DMA_SetDataLength(priv->dma, priv->dma_tx_chnum, chunk);
LL_USART_ClearFlag_TC(priv->periph);
}
LL_DMA_EnableChannel(priv->dma, priv->dma_tx_chnum);
}
COMPILER_ASSERT(UUSART_TXBUF_LEN <= 256); // more would break the "len tag" algorithm
/**
* Put data on the queue. Only a part may be sent due to a buffer size limit.
*
* @param priv
* @param buffer - buffer to send
* @param len - buffer size
* @return number of bytes that were really written (from the beginning)
*/
uint16_t UUSART_DMA_TxQueue(struct priv *priv, const uint8_t *buffer, uint16_t len)
{
const uint16_t nr = priv->tx_buf_nr;
uint16_t nw = priv->tx_buf_nw;
// shortcut for checking a completely full buffer
if (nw == nr-1 || (nr==0&&nw==UUSART_TXBUF_LEN-1)) {
dbg_uusart("Buffer full, cant queue");
return 0;
}
dbg_uusart("\r\nQueue..");
uint16_t used = 0;
if (nr == nw) {
used = 0;
} else if (nw > nr) { // simple linear
used = (uint16_t) (nw - nr);
} else if (nw < nr) { // wrapped
used = (uint16_t) ((UUSART_TXBUF_LEN - nr) + nw);
}
dbg_uusart("Trying to send buffer of len %d", (int)len);
uint16_t avail = (const uint16_t) (UUSART_TXBUF_LEN - 1 - used);
dbg_uusart("nr %d, nw %d, used %d, avail %d", (int)nr, (int)nw, (int)used, (int)avail);
// hack to avoid too large chunks (we use 1 byte to store chunk size)
if (avail > 255) avail = 255;
uint8_t written = 0;
// this avoids attempting to write if we don't have space
if (avail <= 5) {
dbg_uusart("No space (only %d)", (int) avail);
return written;
}
int cnt = 0;
while (avail > 0 && written < len) {
assert_param(cnt < 2); // if more than two, we have a bug and it's repeating infinitely
cnt++;
// Padding with chunk information (1 byte: length) - for each chunk
const uint8_t lpad = 1;
// Chunk can go max to the end of the buffer
uint8_t chunk = (uint8_t) MIN((len-written) + lpad, UUSART_TXBUF_LEN - nw);
if (chunk > avail) chunk = (uint8_t) avail;
dbg_uusart("nw %d, raw available chunk %d", (int) nw, (int)chunk);
if (chunk < lpad + 1) {
// write 0 to indicate a wrap-around
dbg_uusart("Wrap-around marker at offset %d", (int) nw);
priv->tx_buffer[nw] = 0;
nw = 0;
}
else {
// enough space for a preamble + some data
dbg_uusart("Preamble of %d bytes at offset %d", (int) lpad, (int) nw);
priv->tx_buffer[nw] = (uint8_t) (chunk - lpad);
nw += lpad;
uint8_t datachunk = (uint8_t) (chunk - lpad);
dbg_uusart("Datachunk len %d at offset %d", (int) datachunk, (int) nw);
//#if UUSART_DEBUG
// PUTS("mcpy src >"); PUTSN((char *) (buffer), datachunk); PUTS("<\r\n");
//#endif
memcpy((uint8_t *) (priv->tx_buffer + nw), buffer, datachunk);
//#if UUSART_DEBUG
// PUTS("mcpy dst >"); PUTSN((char *) (priv->tx_buffer + nw), datachunk); PUTS("<\r\n");
//#endif
buffer += datachunk;
nw += datachunk;
written += datachunk;
if (nw == UUSART_TXBUF_LEN) nw = 0;
}
avail -= chunk;
dbg_uusart(". end of loop, avail is %d", (int)avail);
}
{
dbg_uusart("Write done -> nr %d, nw %d", (int) nr, (int) nw);
// FIXME a potential race condition can happen here (but it's unlikely)
priv->tx_buf_nw = nw;
if (!priv->tx_dma_busy) {
dbg_uusart("Write done, requesting DMA.");
UUSART_DMA_TxStart(priv);
}
else {
dbg_uusart("DMA in progress, not requesting");
}
}
return written;
}
/**
* Handler for the Tx DMA - completion interrupt
* @param arg - unit instance
*/
static void UUSART_DMA_TxHandler(void *arg)
{
Unit *unit = arg;
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
uint32_t isrsnapshot = priv->dma->ISR;
if (LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_tx_chnum)) {
// chunk Tx is finished
dbg_uusart("~ DMA tx done, nr %d, nw %d, chunk %d", (int)priv->tx_buf_nr, (int)priv->tx_buf_nw, (int)priv->tx_buf_chunk);
priv->tx_buf_nr += priv->tx_buf_chunk;
if (UUSART_TXBUF_LEN == priv->tx_buf_nr) priv->tx_buf_nr = 0;
priv->tx_buf_chunk = 0;
LL_DMA_ClearFlag_TC(priv->dma, priv->dma_tx_chnum);
// Wait for TC
while (!LL_USART_IsActiveFlag_TC(priv->periph)); // TODO timeout
// start the next chunk
if (priv->tx_buf_nr != priv->tx_buf_nw) {
dbg_uusart(" Asking for more, if any");
UUSART_DMA_TxStart(priv);
} else {
priv->tx_dma_busy = false;
}
}
}
void UUSART_DeInitDMAs(Unit *unit)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
irqd_detach(priv->dma_tx, UUSART_DMA_TxHandler);
irqd_detach(priv->dma_rx, UUSART_DMA_RxHandler);
LL_DMA_DeInit(priv->dma, priv->dma_rx_chnum);
LL_DMA_DeInit(priv->dma, priv->dma_tx_chnum);
free_ck(priv->rx_buffer);
free_ck(priv->tx_buffer);
}
+344
View File
@@ -0,0 +1,344 @@
//
// Created by MightyPork on 2018/01/14.
//
#include "platform.h"
#include "unit_base.h"
#define UUSART_INTERNAL
#include "_usart_internal.h"
extern error_t UUSART_ClaimDMAs(Unit *unit);
extern error_t UUSART_SetupDMAs(Unit *unit);
extern void UUSART_DeInitDMAs(Unit *unit);
/** Allocate data structure and set defaults */
error_t UUSART_preInit(Unit *unit)
{
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv));
if (priv == NULL) return E_OUT_OF_MEM;
// some defaults
priv->periph_num = 1;
priv->remap = 0;
priv->baudrate = 115200;
priv->parity = 0; //!< 0-none, 1-odd, 2-even
priv->stopbits = 1; //!< 0-half, 1-one, 2-1.5, 3-two
priv->direction = UUSART_DIRECTION_RXTX; // RXTX
priv->hw_flow_control = false;
priv->clock_output = false;
priv->cpol = 0;
priv->cpha = 0;
priv->lsb_first = true; // LSB first is default for UART
priv->width = 8;
priv->data_inv = false;
priv->rx_inv = false;
priv->tx_inv = false;
priv->de_output = false;
priv->de_polarity = 1; // active high
// this should equal to a half-byte length when oversampling by 16 is used (default)
priv->de_assert_time = 8;
priv->de_clear_time = 8;
return E_SUCCESS;
}
/** Claim the peripheral and assign priv->periph */
static inline error_t UUSART_claimPeriph(Unit *unit)
{
struct priv *priv = unit->data;
if (!(priv->periph_num >= 1 && priv->periph_num <= 5)) {
dbg("!! Bad USART periph");
return E_BAD_CONFIG;
}
// assign and claim the peripheral
if (priv->periph_num == 1) {
TRY(rsc_claim(unit, R_USART1));
priv->periph = USART1;
}
else if (priv->periph_num == 2) {
TRY(rsc_claim(unit, R_USART2));
priv->periph = USART2;
}
else if (priv->periph_num == 3) {
TRY(rsc_claim(unit, R_USART3));
priv->periph = USART3;
}
#if defined(USART4)
else if (priv->periph_num == 4) {
TRY(rsc_claim(unit, R_USART4));
priv->periph = USART4;
}
#endif
#if defined(USART5)
else if (priv->periph_num == 5) {
TRY(rsc_claim(unit, R_USART5));
priv->periph = USART5;
}
#endif
else return E_BAD_CONFIG;
TRY(UUSART_ClaimDMAs(unit));
return E_SUCCESS;
}
/** Claim and configure GPIOs used */
static inline error_t UUSART_configPins(Unit *unit)
{
struct priv *priv = unit->data;
// This is written for F072, other platforms will need adjustments
// Configure UART pins (AF)
#define want_ck_pin(priv) ((priv)->clock_output)
#define want_tx_pin(priv) (bool)((priv)->direction & 2)
#define want_rx_pin(priv) (bool)((priv)->direction & 1)
#define want_cts_pin(priv) ((priv)->hw_flow_control==2 || (priv)->hw_flow_control==3)
#define want_rts_pin(priv) ((priv)->de_output || (priv)->hw_flow_control==1 || (priv)->hw_flow_control==3)
/* List of required pins based on the user config */
bool pins_wanted[5] = {
want_ck_pin(priv),
want_tx_pin(priv),
want_rx_pin(priv),
want_cts_pin(priv),
want_rts_pin(priv)
};
#if STM32F072xB
const struct PinAF *mappings = NULL;
// TODO adjust this, possibly remove / split to individual pin config for ..
// the final board
const struct PinAF mapping_1_0[5] = {
{'A', 8, LL_GPIO_AF_1}, // CK
{'A', 9, LL_GPIO_AF_1}, // TX
{'A', 10, LL_GPIO_AF_1}, // RX
{'A', 11, LL_GPIO_AF_1}, // CTS - collides with USB
{'A', 12, LL_GPIO_AF_1}, // RTS - collides with USB
};
const struct PinAF mapping_1_1[5] = {
{'A', 8, LL_GPIO_AF_1}, // CK*
{'B', 6, LL_GPIO_AF_1}, // TX
{'B', 7, LL_GPIO_AF_1}, // RX
{'A', 11, LL_GPIO_AF_1}, // CTS* - collides with USB
{'A', 12, LL_GPIO_AF_1}, // RTS* - collides with USB
};
const struct PinAF mapping_2_0[5] = {
{'A', 4, LL_GPIO_AF_1}, // CK
{'A', 2, LL_GPIO_AF_1}, // TX
{'A', 3, LL_GPIO_AF_1}, // RX
{'A', 0, LL_GPIO_AF_1}, // CTS
{'A', 1, LL_GPIO_AF_1}, // RTS
};
const struct PinAF mapping_2_1[5] = {
{'A', 4, LL_GPIO_AF_1}, // CK*
{'A', 14, LL_GPIO_AF_1}, // TX
{'A', 15, LL_GPIO_AF_1}, // RX
{'A', 0, LL_GPIO_AF_1}, // CTS*
{'A', 1, LL_GPIO_AF_1}, // RTS*
};
const struct PinAF mapping_3_0[5] = {
{'B', 12, LL_GPIO_AF_4}, // CK
{'B', 10, LL_GPIO_AF_4}, // TX
{'B', 11, LL_GPIO_AF_4}, // RX
{'B', 13, LL_GPIO_AF_4}, // CTS
{'B', 14, LL_GPIO_AF_4}, // RTS
};
const struct PinAF mapping_4_0[5] = {
{'C', 12, LL_GPIO_AF_0}, // CK
{'A', 0, LL_GPIO_AF_4}, // TX
{'A', 1, LL_GPIO_AF_4}, // RX
{'B', 7, LL_GPIO_AF_4}, // CTS
{'A', 15, LL_GPIO_AF_4}, // RTS
};
const struct PinAF mapping_4_1[5] = {
{'C', 12, LL_GPIO_AF_0}, // CK*
{'C', 10, LL_GPIO_AF_0}, // TX
{'C', 11, LL_GPIO_AF_0}, // RX
{'B', 7, LL_GPIO_AF_4}, // CTS*
{'A', 15, LL_GPIO_AF_4}, // RTS*
};
if (priv->periph_num == 1) {
// USART1
if (priv->remap == 0) mappings = &mapping_1_0[0];
else if (priv->remap == 1) mappings = &mapping_1_1[0];
else return E_BAD_CONFIG;
}
else if (priv->periph_num == 2) {
// USART2
if (priv->remap == 0) mappings = &mapping_2_0[0];
else if (priv->remap == 1) mappings = &mapping_2_1[0];
else return E_BAD_CONFIG;
}
else if (priv->periph_num == 3) {
// USART3
if (priv->remap == 0) mappings = &mapping_3_0[0];
else return E_BAD_CONFIG;
}
else if (priv->periph_num == 4) {
// USART3
if (priv->remap == 0) mappings = &mapping_4_0[0];
else if (priv->remap == 1) mappings = &mapping_4_1[0];
else return E_BAD_CONFIG;
}
else return E_BAD_CONFIG;
// Apply mappings based on the 'wanted' table
for (int i = 0; i < 5; i++) {
if (pins_wanted[i]) {
if (mappings[i].port == 0) return E_BAD_CONFIG;
TRY(rsc_claim_pin(unit, mappings[i].port, mappings[i].pin));
TRY(hw_configure_gpio_af(mappings[i].port, mappings[i].pin, mappings[i].af));
}
}
#elif GEX_PLAT_F103_BLUEPILL
#error "NO IMPL"
#elif GEX_PLAT_F303_DISCOVERY
#error "NO IMPL"
#elif GEX_PLAT_F407_DISCOVERY
#error "NO IMPL"
#else
#error "BAD PLATFORM!"
#endif
return E_SUCCESS;
}
/** Finalize unit set-up */
error_t UUSART_init(Unit *unit)
{
struct priv *priv = unit->data;
TRY(UUSART_claimPeriph(unit));
TRY(UUSART_configPins(unit));
// --- Configure the peripheral ---
// Enable clock for the peripheral used
hw_periph_clock_enable(priv->periph);
LL_USART_Disable(priv->periph);
{
LL_USART_DeInit(priv->periph);
LL_USART_SetBaudRate(priv->periph, PLAT_APB1_HZ, LL_USART_OVERSAMPLING_16, priv->baudrate);
LL_USART_SetParity(priv->periph,
priv->parity == 0 ? LL_USART_PARITY_NONE :
priv->parity == 1 ? LL_USART_PARITY_ODD
: LL_USART_PARITY_EVEN);
LL_USART_SetStopBitsLength(priv->periph,
priv->stopbits == 0 ? LL_USART_STOPBITS_0_5 :
priv->stopbits == 1 ? LL_USART_STOPBITS_1 :
priv->stopbits == 2 ? LL_USART_STOPBITS_1_5
: LL_USART_STOPBITS_2);
LL_USART_SetTransferDirection(priv->periph,
(priv->direction == UUSART_DIRECTION_RX) ? LL_USART_DIRECTION_RX :
(priv->direction == UUSART_DIRECTION_TX) ? LL_USART_DIRECTION_TX
: LL_USART_DIRECTION_TX_RX);
LL_USART_SetHWFlowCtrl(priv->periph,
priv->hw_flow_control == 0 ? LL_USART_HWCONTROL_NONE :
priv->hw_flow_control == 1 ? LL_USART_HWCONTROL_RTS :
priv->hw_flow_control == 2 ? LL_USART_HWCONTROL_CTS
: LL_USART_HWCONTROL_RTS_CTS);
LL_USART_ConfigClock(priv->periph,
priv->cpha ? LL_USART_PHASE_2EDGE : LL_USART_PHASE_1EDGE,
priv->cpol ? LL_USART_POLARITY_HIGH : LL_USART_POLARITY_LOW,
true); // clock on last bit - TODO configurable?
if (priv->clock_output)
LL_USART_EnableSCLKOutput(priv->periph);
else
LL_USART_DisableSCLKOutput(priv->periph);
LL_USART_SetTransferBitOrder(priv->periph,
priv->lsb_first ? LL_USART_BITORDER_LSBFIRST
: LL_USART_BITORDER_MSBFIRST);
LL_USART_SetDataWidth(priv->periph,
priv->width == 7 ? LL_USART_DATAWIDTH_7B :
priv->width == 8 ? LL_USART_DATAWIDTH_8B
: LL_USART_DATAWIDTH_9B);
LL_USART_SetBinaryDataLogic(priv->periph,
priv->data_inv ? LL_USART_BINARY_LOGIC_NEGATIVE
: LL_USART_BINARY_LOGIC_POSITIVE);
LL_USART_SetRXPinLevel(priv->periph, priv->rx_inv ? LL_USART_RXPIN_LEVEL_INVERTED
: LL_USART_RXPIN_LEVEL_STANDARD);
LL_USART_SetTXPinLevel(priv->periph, priv->tx_inv ? LL_USART_TXPIN_LEVEL_INVERTED
: LL_USART_TXPIN_LEVEL_STANDARD);
if (priv->de_output)
LL_USART_EnableDEMode(priv->periph);
else
LL_USART_DisableDEMode(priv->periph);
LL_USART_SetDESignalPolarity(priv->periph,
priv->de_polarity ? LL_USART_DE_POLARITY_HIGH
: LL_USART_DE_POLARITY_LOW);
LL_USART_SetDEAssertionTime(priv->periph, priv->de_assert_time);
LL_USART_SetDEDeassertionTime(priv->periph, priv->de_clear_time);
// Prepare for DMA
LL_USART_ClearFlag_TC(priv->periph);
LL_USART_EnableDMAReq_RX(priv->periph);
LL_USART_EnableDMAReq_TX(priv->periph);
}
LL_USART_Enable(priv->periph);
// modifies some usart registers that can't be modified when enabled
TRY(UUSART_SetupDMAs(unit));
// timeout based on the baudrate
unit->tick_interval = (uint16_t) ((50 * 1000) / priv->baudrate); // receive timeout (ms)
if (unit->tick_interval < 5) unit->tick_interval = 5;
return E_SUCCESS;
}
/** Tear down the unit */
void UUSART_deInit(Unit *unit)
{
struct priv *priv = unit->data;
// de-init the pins & peripheral only if inited correctly
if (unit->status == E_SUCCESS) {
assert_param(priv->periph);
LL_USART_DeInit(priv->periph);
// Disable clock
hw_periph_clock_disable(priv->periph);
UUSART_DeInitDMAs(unit);
}
// Release all resources
rsc_teardown(unit);
// Free memory
free_ck(unit->data);
unit->data = NULL;
}
+118
View File
@@ -0,0 +1,118 @@
//
// Created by MightyPork on 2018/01/14.
//
// Internal defines and consts used by the USART unit.
// Can be included only by the USART c files.
//
#ifndef GEX_F072_UUSART_INTERNAL_H
#define GEX_F072_UUSART_INTERNAL_H
#ifndef UUSART_INTERNAL
#error "Bad include"
#endif
#include "platform.h"
#define UUSART_RXBUF_LEN 128
#define UUSART_TXBUF_LEN 128
#define UUSART_DIRECTION_RX 1
#define UUSART_DIRECTION_TX 2
#define UUSART_DIRECTION_RXTX 3
/** Private data structure */
struct priv {
uint8_t periph_num; //!< 1-6
uint8_t remap; //!< UART remap option
uint32_t baudrate; //!< baud rate
uint8_t parity; //!< 0-none, 1-odd, 2-even
uint8_t stopbits; //!< 0-half, 1-one, 2-one-and-half, 3-two (halves - 1)
uint8_t direction; //!< 1-RX, 2-TX, 3-RXTX
uint8_t hw_flow_control; //!< HW flow control 0-none, 1-RTC, 2-CTS, 3-full
bool clock_output; //!< Output serial clock
bool cpol; //!< clock CPOL setting
bool cpha; //!< clock CPHA setting
bool lsb_first; //!< bit order
uint8_t width; //!< word width - 7, 8, 9 (this includes parity)
bool data_inv; //!< Invert data bytes
bool rx_inv; //!< Invert the RX pin levels
bool tx_inv; //!< Invert the TX pin levels
bool de_output; //!< Generate the Driver Enable signal for RS485
bool de_polarity; //!< DE active level
uint8_t de_assert_time; //!< Time to assert the DE signal before transmit
uint8_t de_clear_time; //!< Time to clear the DE signal after transmit
USART_TypeDef *periph; //!< USART peripheral
DMA_TypeDef *dma; //!< DMA peripheral
uint8_t dma_rx_chnum; //!< DMA rx channel number (resolved dynamically based on availability)
uint8_t dma_tx_chnum; //!< DMA tx channel number (resolved dynamically based on availability)
DMA_Channel_TypeDef *dma_rx; //!< DMA rx channel instance
DMA_Channel_TypeDef *dma_tx; //!< DMA tx channel instance
// DMA stuff
volatile uint8_t *rx_buffer; //!< Receive buffer (malloc'd). Has configured TC and TH interrupts.
volatile uint16_t rx_buf_readpos; //!< Start of the next read (sending to USB)
volatile uint16_t rx_last_dmapos; //!< Last position of the DMA cyclic write. Used to detect timeouts and for partial data capture from the buffer
volatile uint8_t *tx_buffer; //!< Transmit buffer (malloc'd)
volatile uint16_t tx_buf_nr; //!< Next Read index
volatile uint16_t tx_buf_nw; //!< Next Write index
volatile uint16_t tx_buf_chunk; //!< Size of the currently being transmitted chunk (for advancing the pointers)
volatile bool tx_dma_busy; //!< Flag that the Tx DMA request is ongoing
};
// ------------------------------------------------------------------------
/** Load from a binary buffer stored in Flash */
void UUSART_loadBinary(Unit *unit, PayloadParser *pp);
/** Write to a binary buffer for storing in Flash */
void UUSART_writeBinary(Unit *unit, PayloadBuilder *pb);
// ------------------------------------------------------------------------
/** Parse a key-value pair from the INI file */
error_t UUSART_loadIni(Unit *unit, const char *key, const char *value);
/** Generate INI file section for the unit */
void UUSART_writeIni(Unit *unit, IniWriter *iw);
// ------------------------------------------------------------------------
/** Allocate data structure and set defaults */
error_t UUSART_preInit(Unit *unit);
/** Tear down the unit */
void UUSART_deInit(Unit *unit);
/** Finalize unit set-up */
error_t UUSART_init(Unit *unit);
/**
* Handle received data (we're inside the IRQ)
*
* @param unit - handled unit
* @param endpos - end position in the buffer
*/
void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos);
/**
* Put data on the queue. Only a part may be sent due to a buffer size limit.
*
* @param priv
* @param buffer - buffer to send
* @param len - buffer size
* @return number of bytes that were really written (from the beginning)
*/
uint16_t UUSART_DMA_TxQueue(struct priv *priv, const uint8_t *buffer, uint16_t len);
// ------------------------------------------------------------------------
#endif //GEX_F072_UUSART_INTERNAL_H
+233
View File
@@ -0,0 +1,233 @@
//
// Created by MightyPork on 2018/01/14.
//
#include "platform.h"
#include "unit_base.h"
#include "unit_usart.h"
#define UUSART_INTERNAL
#include "_usart_internal.h"
/** Load from a binary buffer stored in Flash */
void UUSART_loadBinary(Unit *unit, PayloadParser *pp)
{
struct priv *priv = unit->data;
uint8_t version = pp_u8(pp);
(void)version;
priv->periph_num = pp_u8(pp);
priv->remap = pp_u8(pp);
priv->baudrate = pp_u32(pp);
priv->parity = pp_u8(pp);
priv->stopbits = pp_u8(pp);
priv->direction = pp_u8(pp);
priv->hw_flow_control = pp_u8(pp);
priv->clock_output = pp_bool(pp);
priv->cpol = pp_bool(pp);
priv->cpha = pp_bool(pp);
priv->lsb_first = pp_bool(pp);
priv->width = pp_u8(pp);
priv->data_inv = pp_bool(pp);
priv->rx_inv = pp_bool(pp);
priv->tx_inv = pp_bool(pp);
priv->de_output = pp_bool(pp);
priv->de_polarity = pp_bool(pp);
priv->de_assert_time = pp_u8(pp);
priv->de_clear_time = pp_u8(pp);
}
/** Write to a binary buffer for storing in Flash */
void UUSART_writeBinary(Unit *unit, PayloadBuilder *pb)
{
struct priv *priv = unit->data;
pb_u8(pb, 0); // version
pb_u8(pb, priv->periph_num);
pb_u8(pb, priv->remap);
pb_u32(pb, priv->baudrate);
pb_u8(pb, priv->parity);
pb_u8(pb, priv->stopbits);
pb_u8(pb, priv->direction);
pb_u8(pb, priv->hw_flow_control);
pb_bool(pb, priv->clock_output);
pb_bool(pb, priv->cpol);
pb_bool(pb, priv->cpha);
pb_bool(pb, priv->lsb_first);
pb_u8(pb, priv->width);
pb_bool(pb, priv->data_inv);
pb_bool(pb, priv->rx_inv);
pb_bool(pb, priv->tx_inv);
pb_bool(pb, priv->de_output);
pb_bool(pb, priv->de_polarity);
pb_u8(pb, priv->de_assert_time);
pb_u8(pb, priv->de_clear_time);
}
/** Parse a key-value pair from the INI file */
error_t UUSART_loadIni(Unit *unit, const char *key, const char *value)
{
bool suc = true;
struct priv *priv = unit->data;
if (streq(key, "device")) {
priv->periph_num = cfg_u8_parse(value, &suc);
}
else if (streq(key, "remap")) {
priv->remap = cfg_u8_parse(value, &suc);
}
else if (streq(key, "baud-rate")) {
priv->baudrate = cfg_u32_parse(value, &suc);
}
else if (streq(key, "parity")) {
priv->parity = (uint8_t) cfg_enum3_parse(value,
"NONE", 0,
"ODD", 1,
"EVEN", 2, &suc);
}
else if (streq(key, "stop-bits")) {
priv->stopbits = (uint8_t) cfg_enum4_parse(value,
"0.5", 0,
"1", 1,
"1.5", 2,
"2", 3, &suc);
}
else if (streq(key, "direction")) {
priv->direction = (uint8_t) cfg_enum3_parse(value,
"RX", UUSART_DIRECTION_RX,
"TX", UUSART_DIRECTION_TX,
"RXTX", UUSART_DIRECTION_RXTX, &suc);
}
else if (streq(key, "hw-flow-control")) {
priv->hw_flow_control = (uint8_t) cfg_enum4_parse(value,
"NONE", 0,
"RTS", 1,
"CTS", 2,
"FULL", 3, &suc);
}
else if (streq(key, "word-width")) {
priv->width = cfg_u8_parse(value, &suc);
}
else if (streq(key, "first-bit")) {
priv->lsb_first = (bool) cfg_enum2_parse(value, "MSB", 0, "LSB", 1, &suc);
}
else if (streq(key, "clock-output")) {
priv->clock_output = cfg_bool_parse(value, &suc);
}
else if (streq(key, "cpol")) {
priv->cpol = cfg_bool_parse(value, &suc);
}
else if (streq(key, "cpha")) {
priv->cpha = cfg_bool_parse(value, &suc);
}
else if (streq(key, "de-output")) {
priv->de_output = cfg_bool_parse(value, &suc);
}
else if (streq(key, "de-polarity")) {
priv->de_polarity = cfg_bool_parse(value, &suc);
}
else if (streq(key, "de-assert-time")) {
priv->de_assert_time = cfg_u8_parse(value, &suc);
}
else if (streq(key, "de-clear-time")) {
priv->de_clear_time = cfg_u8_parse(value, &suc);
}
else {
return E_BAD_KEY;
}
if (!suc) return E_BAD_VALUE;
return E_SUCCESS;
}
/** Generate INI file section for the unit */
void UUSART_writeIni(Unit *unit, IniWriter *iw)
{
struct priv *priv = unit->data;
iw_comment(iw, "Peripheral number (UARTx 1-4)");
iw_entry_d(iw, "device", priv->periph_num);
iw_comment(iw, "Pin mappings (TX,RX,CK,CTS,RTS/DE)");
#if STM32F072xB
iw_comment(iw, " USART1: (0) A9,A10,A8,A11,A12 (1) B6,B7,A8,A11,A12");
iw_comment(iw, " USART2: (0) A2,A3,A4,A0,A1 (1) A14,A15,A4,A0,A1");
iw_comment(iw, " USART3: (0) B10,B11,B12,B13,B14");
iw_comment(iw, " USART4: (0) A0,A1,C12,B7,A15 (1) C10,C11,C12,B7,A15");
#elif GEX_PLAT_F103_BLUEPILL
#error "NO IMPL"
#elif GEX_PLAT_F303_DISCOVERY
#error "NO IMPL"
#elif GEX_PLAT_F407_DISCOVERY
#error "NO IMPL"
#else
#error "BAD PLATFORM!"
#endif
iw_entry_d(iw, "remap", priv->remap);
iw_cmt_newline(iw);
iw_comment(iw, "Baud rate in bps (eg. 9600)");
iw_entry_d(iw, "baud-rate", priv->baudrate);
iw_comment(iw, "Parity type (NONE, ODD, EVEN)");
iw_entry_s(iw, "parity", cfg_enum3_encode(priv->parity,
0, "NONE",
1, "ODD",
2, "EVEN"));
iw_comment(iw, "Number of stop bits (0.5, 1, 1.5, 2)");
iw_entry_s(iw, "stop-bits", cfg_enum4_encode(priv->stopbits,
0, "0.5",
1, "1",
2, "1.5",
3, "2"));
iw_comment(iw, "Bit order (LSB or MSB first)");
iw_entry_s(iw, "first-bit", cfg_enum2_encode((uint32_t) priv->lsb_first,
0, "MSB",
1, "LSB"));
iw_comment(iw, "Word width (7,8,9) - including parity bit if used");
iw_entry_d(iw, "word-width", (int)priv->width);
iw_comment(iw, "Enabled lines (RX,TX,RXTX)");
iw_entry_s(iw, "direction", cfg_enum3_encode(priv->direction,
1, "RX",
2, "TX",
3, "RXTX"));
iw_comment(iw, "Hardware flow control (NONE, RTS, CTS, FULL)");
iw_entry_s(iw, "hw-flow-control", cfg_enum4_encode(priv->hw_flow_control,
0, "NONE",
1, "RTS",
2, "CTS",
3, "FULL"));
iw_cmt_newline(iw);
iw_comment(iw, "Generate serial clock (Y,N)");
iw_entry_s(iw, "clock-output", str_yn(priv->clock_output));
iw_comment(iw, "Clock polarity: 0,1");
iw_entry_d(iw, "cpol", priv->cpol);
iw_comment(iw, "Clock phase: 0,1");
iw_entry_d(iw, "cpha", priv->cpha);
iw_cmt_newline(iw);
iw_comment(iw, "Generate RS485 Driver Enable signal (Y,N) - uses RTS pin");
iw_entry_s(iw, "de-output", str_yn(priv->de_output));
iw_comment(iw, "DE active level: 0,1");
iw_entry_d(iw, "de-polarity", (priv->de_polarity));
iw_comment(iw, "DE assert time (0-31)");
iw_entry_d(iw, "de-assert-time", (priv->de_assert_time));
iw_comment(iw, "DE clear time (0-31)");
iw_entry_d(iw, "de-clear-time", (priv->de_clear_time));
}
+146
View File
@@ -0,0 +1,146 @@
//
// Created by MightyPork on 2018/01/02.
//
#include "unit_base.h"
#include "unit_usart.h"
#define UUSART_INTERNAL
#include "_usart_internal.h"
/**
* Data RX handler, run on the jobs thread.
*
* unit - unit
* timestamp - timestamp
* data1 - read start position
* data2 - nr of bytes to read
*
* @param job
*/
static void UUSART_SendReceivedDataToMaster(Job *job)
{
Unit *unit = job->unit;
struct priv *priv = unit->data;
uint16_t readpos = (uint16_t) job->data1;
uint16_t count = (uint16_t) job->data2;
EventReport event = {
.unit = job->unit,
.timestamp = job->timestamp,
.data = (uint8_t *) (priv->rx_buffer + readpos),
.length = count,
};
EventReport_Send(&event);
}
/**
* Handle received data (we're inside the IRQ).
* This is called either from a DMA complete / half interrupot,
* or form the timeout interrupt.
*
* @param unit - handled unit
* @param endpos - end position in the buffer
*/
void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos)
{
const uint64_t ts = PTIM_GetMicrotime();
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
uint16_t readpos = priv->rx_buf_readpos;
assert_param(endpos > readpos);
uint16_t count = (endpos - readpos);
// We defer it to the job queue
Job j = {
.unit = unit,
.timestamp = ts,
.data1 = priv->rx_buf_readpos,
.data2 = count,
.cb = UUSART_SendReceivedDataToMaster
};
scheduleJob(&j); // TODO disable unit on failure
// Move the read cursor, wrap around if needed
if (endpos == UUSART_RXBUF_LEN) endpos = 0;
priv->rx_buf_readpos = endpos;
}
/**
* Timed tick (ISR) - check timeout
*/
void UUSART_Tick(Unit *unit)
{
assert_param(unit);
struct priv *priv = unit->data;
assert_param(priv);
if (priv->rx_last_dmapos == priv->dma_rx->CNDTR) {
uint16_t endpos = (uint16_t) (UUSART_RXBUF_LEN - priv->rx_last_dmapos);
if (endpos != priv->rx_buf_readpos) {
UUSART_DMA_HandleRxFromIRQ(unit, endpos);
}
} else {
priv->rx_last_dmapos = (uint16_t) priv->dma_rx->CNDTR;
}
}
enum PinCmd_ {
CMD_WRITE = 0,
CMD_WRITE_SYNC = 1,
};
/** Handle a request message */
static error_t UUSART_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp)
{
uint32_t len;
const uint8_t *pld;
switch (command) {
/**
* Write bytes to the USART, without waiting for completion.
* May wait until there is space in the DMA buffer.
*/
case CMD_WRITE:
pld = pp_tail(pp, &len);
TRY(UU_USART_Write(unit, pld, len));
return E_SUCCESS;
/**
* Write bytes to the USART. Payload consists of the data to send.
* Waits for completion.
*/
case CMD_WRITE_SYNC:
pld = pp_tail(pp, &len);
TRY(UU_USART_WriteSync(unit, pld, len));
return E_SUCCESS;
default:
return E_UNKNOWN_COMMAND;
}
}
// ------------------------------------------------------------------------
/** Unit template */
const UnitDriver UNIT_USART = {
.name = "USART",
.description = "Serial port",
// Settings
.preInit = UUSART_preInit,
.cfgLoadBinary = UUSART_loadBinary,
.cfgWriteBinary = UUSART_writeBinary,
.cfgLoadIni = UUSART_loadIni,
.cfgWriteIni = UUSART_writeIni,
// Init
.init = UUSART_init,
.deInit = UUSART_deInit,
// Function
.updateTick = UUSART_Tick,
.handleRequest = UUSART_handleRequest,
};
+39
View File
@@ -0,0 +1,39 @@
//
// Created by MightyPork on 2018/01/02.
//
// USART driver using DMA buffers.
// Provides a wide range of config options and supports driving a RS485 driver.
//
// The implementation is complex and split to multiple files for easier maintenance
//
#ifndef GEX_F072_UNIT_USART_H
#define GEX_F072_UNIT_USART_H
#include "unit.h"
extern const UnitDriver UNIT_USART;
/**
* Write bytes. This function is asynchronous and does not wait for completion.
* It blocks until there's space in the Tx buffer for the data.
*
* @param unit
* @param buffer - bytes to send
* @param len - number of bytes to send
* @return success
*/
error_t UU_USART_Write(Unit *unit, const uint8_t *buffer, uint32_t len);
/**
* Write bytes. Same like UU_USART_Write(), except it waits for the transmission
* to complete after sending the last data.
*
* @param unit
* @param buffer - bytes to send
* @param len - number of bytes to send
* @return success
*/
error_t UU_USART_WriteSync(Unit *unit, const uint8_t *buffer, uint32_t len);
#endif //GEX_F072_UNIT_USART_H