RISC-V/V203/usb/ch32v203/usart.cpp
2024-10-18 19:45:45 +02:00

90 lines
3 KiB
C++

#include "system.h"
#include "../common/usart.h"
static Usart * pInstance = nullptr;
extern "C" {
[[gnu::interrupt]] void USART1_IRQHandler (void);
};
void USART1_IRQHandler (void) {
if (pInstance) pInstance->irq();
};
Usart::Usart(const uint32_t _baud) noexcept : BaseLayer (), tx_ring () {
pInstance = this;
// 1. Clock Enable
RCC.APB2PCENR.modify([](RCC_Type::APB2PCENR_DEF & r) -> auto {
r.B.USART1EN = SET;
r.B.IOPAEN = SET;
//r.B.AFIOEN = SET; // není nutné
return r.R;
});
// 2. GPIO Alternate Config - default TX/PA9, RX/PA10
GPIOA.CFGHR.modify([](GPIOA_Type::CFGHR_DEF & r) -> auto {
r.B.MODE9 = 1u;
r.B.CNF9 = 2u; // or 3u for open drain
r.B.MODE10 = 0u;
r.B.CNF10 = 1u; // floating input
return r.R;
});
RCC.APB2PRSTR.B.USART1RST = SET;
RCC.APB2PRSTR.B.USART1RST = RESET;
// 5. USART registry 8.bit bez parity
USART1.CTLR1.modify([] (USART1_Type::CTLR1_DEF & r) -> auto {
r.B.RE = SET;
r.B.TE = SET;
r.B.RXNEIE = SET;
return r.R;
});
USART1.CTLR2.modify ([](USART1_Type::CTLR2_DEF & r) -> auto {
r.B.STOP = 0u;
return r.R;
});
SetBaud(_baud);
// NVIC
NVIC.EnableIRQ (USART1_IRQn);
USART1.CTLR1.B.UE = SET; // nakonec povolit globálně
}
void Usart::irq () {
volatile USART1_Type::STATR_DEF status (USART1.STATR); // načti status přerušení
char rdata, tdata;
if (status.B.TXE) { // od vysílače
if (tx_ring.Read (tdata)) { // pokud máme data
USART1.DATAR.B.DR = (uint8_t) tdata; // zapíšeme do výstupu
} else { // pokud ne
USART1.CTLR1.B.TXEIE = RESET;
}
}
if (status.B.RXNE) { // od přijímače
rdata = (USART1.DATAR.B.DR); // načteme data
Up (&rdata, 1u); // a pošleme dál
}
}
uint32_t Usart::Down(const char * data, const uint32_t len) {
unsigned n = 0u;
for (n=0u; n<len; n++) {
if (!tx_ring.Write(data[n])) break;
}
USART1.CTLR1.B.TXEIE = SET; // po povolení přerušení okamžitě přeruší
return n;
}
void Usart::SetBaud (const uint32_t _baud) const {
if (_baud == 0u) return; // ! zero divide
const ONE_BIT b = USART1.CTLR1.B.UE;
if (b == SET) USART1.CTLR1.B.UE = RESET;
const uint32_t HCLK = SystemCoreClock; // hodiny pro USART zde nejsou děleny
const uint32_t tmp = HCLK / _baud; // lze je tedy zapsat přímo
USART1.BRR.R = tmp;
if (b == SET) USART1.CTLR1.B.UE = SET;
}
bool Usart::IOCtrl (const CTRL_TYPES_DEF type, const void * data, const uint32_t len) {
switch (type) {
case USB_USART_SET_PARAM : {
const USB_CDC_LineCoding * lc = reinterpret_cast<const USB_CDC_LineCoding *>(data);
SetBaud(lc->baud); // Pouze změníme rychlost, ostatní parametry ingorujme
} return true;
// Ostatní IOCtrl zatím nebudeme obsluhovat, je to celkem zbytečné
default : break;
};
return false;
}