RISC-V/stm32f051/usart.cpp

111 lines
4.2 KiB
C++
Raw Normal View History

2024-03-07 13:46:47 +01:00
#include "STM32F0x1.h"
#include "CortexM0.h" // NVIC_EnableIRQ
#include "gpio.h"
#include "usart.h"
extern "C" uint32_t SystemCoreClock;
static Usart * Instance = nullptr;
void Usart::irq (void) {
volatile USART1_Type::ISR_DEF status (USART1.ISR); // načti status přerušení
char tdata;
volatile char rdata;
if (status.B.TC) { // od vysílače
if (tx_ring.Read (tdata)) { // pokud máme data
USART1.TDR.R = (uint32_t) tdata & 0xFFu;// zapíšeme do výstupu
} else { // pokud ne
//USART1.CR1.B.RE = SET; // povol prijem
USART1.CR1.B.TCIE = RESET; // je nutné zakázat přerušení od vysílače
}
}
if (status.B.RXNE) { // od přijímače
rdata = (USART1.RDR.R) & 0xFFu; // načteme data
(void) rdata; // zahodime
}
}
/// Voláno z čistého C - startup.c
extern "C" void USART1_IRQHandler (void) {
if (Instance) Instance->irq();
};
//! [MembersConstructorExample]
Usart::Usart(const uint32_t baud) noexcept : BaseLayer(), tx_ring() {
//! [MembersConstructorExample]
if (Instance) return; // Chyba - jedina instance
Instance = this;
// 1. Clock Enable
RCC.APB2ENR.B.USART1EN = SET;
// 2. GPIO Alternate Config
GpioClass txp (GpioPortA, 9, GPIO_Mode_AF);
GpioClass rxp (GpioPortA, 10, GPIO_Mode_AF);
txp.setAF (1);
rxp.setAF (1);
// 4. NVIC
NVIC_EnableIRQ (USART1_IRQn);
uint32_t tmp = 0;
// 5. USART registry 8.bit bez parity
USART1.CR1.modify([] (USART1_Type::CR1_DEF & r) -> uint32_t { // pro ilustraci, co by bylo auto
r.B.TE = SET;
//r.B.RE = SET; // příjem je zde zbytečný
//r.B.RXNEIE = SET;
return r.R;
});
USART1.CR2.R = 0;
USART1.CR3.B.OVRDIS = SET;
// Tuhle část už vezmeme přímo z knihovny, jen ty hodiny zjednodušíme na SystemCoreClock
uint32_t apbclock = SystemCoreClock;
uint32_t integerdivider, fractionaldivider;
/* Determine the integer part */
if (USART1.CR1.B.OVER8 != RESET) {
/* Integer part computing in case Oversampling mode is 8 Samples */
integerdivider = ((25u * apbclock) / (2u * (baud)));
} else {
/* Integer part computing in case Oversampling mode is 16 Samples */
integerdivider = ((25u * apbclock) / (4u * (baud)));
}
tmp = (integerdivider / 100u) << 4;
/* Determine the fractional part */
fractionaldivider = integerdivider - (100u * (tmp >> 4));
/* Implement the fractional part in the register */
if (USART1.CR1.B.OVER8 != RESET) {
tmp |= ((((fractionaldivider * 8u ) + 50u) / 100u)) & ((uint8_t)0x07u);
} else {
tmp |= ((((fractionaldivider * 16u) + 50u) / 100u)) & ((uint8_t)0x0Fu);
}
/* Write to USART BRR */
USART1.BRR.R = (uint16_t)tmp;
USART1.CR1.B.UE = SET; // nakonec povolit globálně
}
//! [VirtualMethodBottom]
uint32_t Usart::Down (const char * data, const uint32_t len) {
uint32_t res; // výsledek, musí žít i po ukončení smyčky
for (res=0; res<len; res++) if (!tx_ring.Write(data[res])) break;
USART1.CR1.B.TCIE = SET; // po povolení přerušení okamžitě přeruší
return res;
}
//! [VirtualMethodBottom]
void Usart::SetHalfDuplex (const bool on) const {
USART1.CR1.B.UE = RESET; // zakázat, jinak nelze nastavovat
if (on) USART1.CR3.B.HDSEL = SET; // poloduplex on
else USART1.CR3.B.HDSEL = RESET; // poloduplex off
USART1.CR1.B.UE = SET; // nakonec povolit globálně
}
void Usart::SetRS485 (const bool polarity) const {
USART1.CR1.B.UE = RESET; // zakázat, jinak nelze nastavovat
// Nastavit pin DE (RTS)
GpioClass de (GpioPortA, 12u, GPIO_Mode_AF);
de.setAF (1u);
// Nastavení driveru
USART1.CR3.B.DEM = SET; // povolit DE v USARTu
if (polarity) USART1.CR3.B.DEP = SET;
else USART1.CR3.B.DEP = RESET;
// A nakonec doby vybavení (přesah) - to je hodně užitečné
//! [LambdaExampleUsage]
USART1.CR1.modify([] (auto & r) -> auto {
r.B.DEAT = 1u; // doba vybavení před start bitem - 16 ~= 1 bit, 0..31
r.B.DEDT = 1u; // doba vybavení po stop bitu - 16 ~= 1 bit, 0..31
return r.R;
});
//! [LambdaExampleUsage]
USART1.CR1.B.UE = SET;
}