add cdc usart

This commit is contained in:
Kizarm 2024-10-15 19:26:19 +02:00
parent b345a72511
commit 07c30fffc8
16 changed files with 620 additions and 0 deletions

View file

@ -0,0 +1,89 @@
#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 {
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;
}

View file

@ -7,6 +7,7 @@ enum CTRL_TYPES_DEF {
USB_USART_SET_DTR_RTS,
USB_USART_INIT,
};
static_assert (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "Bad ENDIAN");
struct USB_CDC_LineCoding {
uint32_t baud;
uint8_t stop, parity, data;

60
V203/usb/common/mirror.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef MIRROR_H
#define MIRROR_H
#include <stdio.h>
#include "baselayer.h"
/**
* @file
* @brief Obraceč datového toku.
* @class Mirror
* @brief Obraceč datového toku 2 třídy.
*
* TwoTop je vlastní obraceč, dědí vlastnosti BaseLayer, pouze metoda Up je přetížena -
* to je to vlastní převrácení. Hlavní třída Mirror, jejíž instance je pak v kódu použita
* obsahuje 2 rovnocenné instance třídy TwoTop, které musí být ve stacku vždy navrchu.
* První část stacku volá Up jedné instance, druhá Up druhé. Proto tam musí být dvě.
* Podobně by šel udělat něco jako Fork, odbočení datového toku.
* A zase - všechny metody jsou tak jednoduché, že jsou celé v hlavičce, tedy inline.
*
* @class TwoTop
* @brief Vlastní obraceč.
* Ono to vypadá hodně zmateně, ale používá se to snadno - dokonce to funguje.
*
*/
class TwoTop : public BaseLayer {
public:
/// Konstruktor
explicit TwoTop () noexcept : BaseLayer (), x (nullptr) {};
/// Setter pro x
void setX (BaseLayer & bl) { x = & bl; };
/// Přetížení metody Up, nic jiného není potřeba
uint32_t Up (const char* data, uint32_t len) {
if (!x) return 0; // pro jistotu
// To, co přišlo zespoda, pošlu druhou instancí zase dolů
return x->Down (data, len);
};
private:
/// Fakticky ukazatel na druhou instanci TwoTop
BaseLayer * x;
};
class Mirror {
public:
/// Konstruktor
explicit Mirror () noexcept : L(), R() {
L.setX (R); R.setX (L);
};
/**
Zřetězení voláme 2x. Poprvé pro jednu +=, podruhé -= pro druhou instanci TwoTop.
Protože je tato třída navrchu (vlevo), operátor nic nevrací.
*/
void operator += (BaseLayer& bl) { L += bl; return; };
void operator -= (BaseLayer& bl) { R += bl; return; };
private:
TwoTop L, R; //!< 2 instance Top
};
#endif // MIRROR_H

22
V203/usb/common/usart.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef USART_H
#define USART_H
#include "fifo.h"
#include "baselayer.h"
#include "ctrlinterface.h"
/** @class Usart
* @brief Sériový port.
*
*/
class Usart : public BaseLayer, public CDC_CtrlInterface {
FIFO<char, 64> tx_ring;
public:
explicit Usart (const uint32_t baud = 9600) noexcept;
uint32_t Down (const char * data, const uint32_t len) override;
void SetBaud (const uint32_t baud) const;
// Obsluha přerušení musí být veřejná.
void irq (void);
// Přetížení pro CDC.
bool IOCtrl (const CTRL_TYPES_DEF type, const void * data, const uint32_t len) override;
};
#endif // USART_H

52
V203/usb/usart/Makefile Normal file
View file

@ -0,0 +1,52 @@
TARGET?= ch32v203
TOOL ?= gcc
#TOOL ?= clang
PRJ = example
VPATH = . ./$(TARGET) ./common
BLD = ./build/
DFLAGS = -d
LFLAGS = -g
LDLIBS =
BFLAGS = --strip-unneeded
CFLAGS = -MMD -Wall -Wno-parentheses -ggdb -fno-exceptions -ffunction-sections -fdata-sections
CFLAGS+= -I. -I./$(TARGET) -I./common
DEL = rm -f
# zdrojaky
OBJS = main.o hack.o
OBJS += usb_desc.o cdc_class.o usart.o
include $(TARGET)/$(TOOL).mk
BOBJS = $(addprefix $(BLD),$(OBJS))
all: $(BLD) $(PRJ).elf
# ... atd.
-include $(BLD)*.d
# linker
$(PRJ).elf: $(BOBJS)
-@echo [LD $(TOOL),$(TARGET)] $@
@$(LD) $(LFLAGS) -o $(PRJ).elf $(BOBJS) $(LDLIBS)
-@echo "size:"
@$(SIZE) $(PRJ).elf
-@echo "listing:"
$(DUMP) $(DFLAGS) $(PRJ).elf > $(PRJ).lst
-@echo "OK."
$(COPY) $(BFLAGS) -O binary $(PRJ).elf $(PRJ).bin
# preloz co je potreba
$(BLD)%.o: %.c
-@echo [CC $(TOOL),$(TARGET)] $@
@$(CC) -std=gnu99 -c $(CFLAGS) $< -o $@
$(BLD)%.o: %.cpp
-@echo [CX $(TOOL),$(TARGET)] $@
@$(CXX) -std=c++17 -fno-rtti -c $(CFLAGS) $< -o $@
$(BLD):
mkdir $(BLD)
flash: $(PRJ).elf
minichlink -w $(PRJ).bin flash -b
# vycisti
clean:
$(DEL) $(BLD)* *.lst *.bin *.elf *.map *~
.PHONY: all clean flash run

1
V203/usb/usart/ch32v203 Symbolic link
View file

@ -0,0 +1 @@
../ch32v203/

1
V203/usb/usart/common Symbolic link
View file

@ -0,0 +1 @@
../common/

21
V203/usb/usart/hack.c Normal file
View file

@ -0,0 +1,21 @@
#include <stdint.h>
#include <stdarg.h>
typedef __SIZE_TYPE__ size_t;
size_t strlen (const char *s) {
size_t l = 0;
while (*s++) l++;
return l;
}
void *memcpy (void *dest, const void *src, size_t n) {
const char *s = (const char *) src;
char *d = (char *) dest;
int i;
for (i=0; i<n; i++) d[i] = s[i];
return dest;
}
void *memset (void *s, int c, size_t n) {
char *p = (char *) s;
int i;
for (i=0; i<n; i++) p[i] = c;
return s;
}

25
V203/usb/usart/main.cpp Normal file
View file

@ -0,0 +1,25 @@
#include "cdc_class.h"
#include "usart.h"
#include "mirror.h"
/** Převodník USB_CDC - USART
* Datový formát je pevný - 8 bit, 1 stop bit, bez parity.
* Změnu po USB by šlo dost jednoduše dodělat, ale nepovažoval jsem to za nutné.
* Baudová rychlost se po USB změnit na libovolnou hodnotu.
*
* Výhoda tohoto řešení je, že nad USARTem může běžet nějaký protokol
* přímo v tomto čipu. Jinak je to asi nejlevnější řešení. Zřejmě to
* nebude snášet velké rychlosti (nad 115200 Bd).
* */
static cdc_class cdc;
static Usart usart;
static Mirror top;
int main () {
cdc.init();
top += cdc;
top -= usart;
cdc.attach(usart);
for (;;) {
/* Vše probíhá v přerušení */
}
return 0;
}

View file

@ -0,0 +1,24 @@
PR = test.elf
CC = gcc
CXX= g++ -std=c++17
AS = as
CFLAGS = -Wall -Os
MFLAGS = -o $(PR)
LFLAGS = -lpthread
all : $(PR)
OBJECTS = main.o usart.o baud.o
$(PR) : $(OBJECTS)
$(CXX) $(MFLAGS) $(OBJECTS) $(LFLAGS)
clean:
rm -f *.o *~
%.o : %.c
$(CC) -c $(CFLAGS) -o $@ $<
%.o : %.cpp
$(CXX) -c $(CFLAGS) -o $@ $<
%.o : %.s
$(AS) -o $@ $<
.PHONY : all clean

View file

@ -0,0 +1,14 @@
#include <asm/termbits.h>
#include <sys/ioctl.h>
#include "usart.h"
void UsartClass::setBaud (int baud) {
struct termios2 tio;
ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = baud;
tio.c_ospeed = baud;
ioctl(fd, TCSETS2, &tio);
}

View file

@ -0,0 +1,45 @@
#include "usart.h"
#include <cstdio>
#include <cstring>
#include <signal.h>
class Top : public BaseLayer {
static constexpr int max = 1024;
volatile unsigned index;
int passcnt;
char buffer [max];
public:
explicit Top () : BaseLayer(), index(0u), passcnt(0) {}
void puts (const char * str) {
const unsigned l = strlen(str);
Down(str, l);
}
uint32_t Up(const char * data, const uint32_t len) override {
for (unsigned n=0; n<len; n++) {
const char c = data [n];
buffer [index++] = c;
if (c == '\n') {
buffer [index - 2] = '\0';
printf("Rx (%d) %s\n", passcnt++, buffer);
index = 0u;
}
}
return len;
}
};
static const char * TestString = "THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG'S BACK 1234567890\r\n";
static volatile bool loop = false;
static void Handler (int) {
loop = false;
}
int main (void) {
loop = true;
signal (SIGINT, Handler);
UsartClass usart ("/dev/serial/by-id/usb-Kizarm_Labs._USB__=__USART_0002-if00", 9600);
Top top;
top += usart;
while (loop) {
top.puts(TestString);
}
return 0;
}

View file

@ -0,0 +1,84 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "usart.h"
UsartClass::UsartClass (const char * name, const int baudrate) : BaseLayer() {
id = name;
running = false;
fd = ::open (id, O_RDWR);
if (fd < 0) return;
timeout = 12'000'000 / baudrate; // cca pro 1 byte
struct termios LineFlags;
int attr = tcgetattr (fd, &LineFlags);
if (attr) {
printf ("Nelze zjistit parametry portu %s\r\n", name);
::close (fd);
return;
}
// nastaveni komunikacnich parametru do struct termios
LineFlags.c_iflag = IGNPAR /* | IXON | IXOFF*/; // ignoruj chyby parity
LineFlags.c_oflag = 0; // normalni nastaveni
LineFlags.c_cflag = CS8 | CREAD | CLOCAL; // 8-bit, povol prijem
LineFlags.c_lflag = 0; // Raw input bez echa
LineFlags.c_cc [VMIN] = 1; // minimalni pocet znaku pro cteni
LineFlags.c_cc [VTIME] = 1; // read timeout 0.1 s
tcsetattr (fd, TCSANOW, &LineFlags);
fcntl (fd, F_SETFL, 0);
int flag = TIOCM_DTR;
ioctl(fd, TIOCMBIS, & flag);
setBaud(baudrate);
printf ("Port %s opened (%d) at %d Bd\r\n", id, fd, baudrate);
usleep (10000);
running = true;
pthread_create (&rc, NULL, UsartClass::UsartHandler, this);
}
UsartClass::~UsartClass() {
running = false;
pthread_cancel (rc);
int flag = TIOCM_DTR;
ioctl(fd, TIOCMBIC, & flag);
::close (fd);
printf ("Port %s closed\r\n", id);
}
uint32_t UsartClass::Up (const char * data, uint32_t len) {
return BaseLayer::Up (data, len);
}
uint32_t UsartClass::Down (const char * data, uint32_t len) {
if (!running) return 0;
const unsigned maxchunk = 32; // USB bulk len (64) je moc
unsigned offset = 0, remain = len;
while (remain) {
const unsigned chunk = remain > maxchunk ? maxchunk : remain;
const unsigned writn = ::write (fd, data + offset, chunk);
usleep (timeout * writn); // závisí na baud rate
offset += writn;
remain -= writn;
}
return offset;
}
void UsartClass::ReadLoop (void) {
int n;
while (running) {
// Nutno číst po jednom nebo použít timer, jinak to po prvním čtení zdechne.
n = ::read (fd, rxbuf, 1);
if (!n) continue;
Up (rxbuf, n);
}
}

View file

@ -0,0 +1,32 @@
#ifndef USARTCLASS_H
#define USARTCLASS_H
#include <pthread.h>
#include "../common/baselayer.h"
static constexpr unsigned BUFLEN = 1024u;
// Bottom
class UsartClass : public BaseLayer {
public:
UsartClass (const char* name, const int baudrate);
uint32_t Up (const char* data, uint32_t len);
uint32_t Down(const char* data, uint32_t len);
virtual ~UsartClass ();
protected:
void ReadLoop (void);
static void* UsartHandler (void* p) {
UsartClass* inst = (UsartClass*) p;
inst->ReadLoop();
return nullptr;
};
void setBaud (int baud);
public:
bool running;
private:
const char * id; //!< Identifikátor třídy pro ladění
char rxbuf[BUFLEN];
int fd;
int timeout;
pthread_t rc;
};
#endif // USARTCLASS_H

View file

@ -0,0 +1,83 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : usb_desc.c
* Author : WCH
* Version : V1.0.0
* Date : 2022/08/20
* Description : usb device descriptor,configuration descriptor,
* string descriptors and other descriptors.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "usb_desc.h"
/* Device Descriptor */
const uint8_t MyDevDescr[] =
{
0x12, // bLength
0x01, // bDescriptorType (Device)
0x10, 0x01, // bcdUSB 1.10
0x02, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
DEF_USBD_UEP0_SIZE, // bMaxPacketSize0 64
(uint8_t)DEF_USB_VID, (uint8_t)(DEF_USB_VID >> 8), // idVendor 0x1A86
(uint8_t)DEF_USB_PID, (uint8_t)(DEF_USB_PID >> 8), // idProduct 0x5537
DEF_IC_PRG_VER, 0x00, // bcdDevice 0.01
0x01, // iManufacturer (String Index)
0x02, // iProduct (String Index)
0x03, // iSerialNumber (String Index)
0x01, // bNumConfigurations 1
};
/* Configuration Descriptor */
const uint8_t MyCfgDescr[] =
{
/* Configure descriptor */
0x09, 0x02, 0x43, 0x00, 0x02, 0x01, 0x00, 0x80, 0x32,
/* Interface 0 (CDC) descriptor */
0x09, 0x04, 0x00, 0x00, 0x01, 0x02, 0x02, 0x01, 0x00,
/* Functional Descriptors */
0x05, 0x24, 0x00, 0x10, 0x01,
/* Length/management descriptor (data class interface 1) */
0x05, 0x24, 0x01, 0x00, 0x01,
0x04, 0x24, 0x02, 0x02,
0x05, 0x24, 0x06, 0x00, 0x01,
/* Interrupt upload endpoint descriptor */
0x07, 0x05, 0x81, 0x03, (uint8_t)DEF_USBD_ENDP1_SIZE, (uint8_t)( DEF_USBD_ENDP1_SIZE >> 8 ), 0x01,
/* Interface 1 (data interface) descriptor */
0x09, 0x04, 0x01, 0x00, 0x02, 0x0A, 0x00, 0x00, 0x00,
/* Endpoint descriptor */
0x07, 0x05, 0x02, 0x02, (uint8_t)DEF_USBD_ENDP2_SIZE, (uint8_t)( DEF_USBD_ENDP2_SIZE >> 8 ), 0x00,
/* Endpoint descriptor */
0x07, 0x05, 0x83, 0x02, (uint8_t)DEF_USBD_ENDP3_SIZE, (uint8_t)( DEF_USBD_ENDP3_SIZE >> 8 ), 0x00,
};
#define DEF_STRDESC(p,n) w_text<(sizeof(p)>>1)>n={sizeof(n)-2u,3u,{p}}
template<const unsigned N> struct w_text {
uint8_t len, typ;
const char16_t str [N];
};
static const DEF_STRDESC((u"Kizarm Labs."), str_1);
static const DEF_STRDESC((u"USB <=> USART"),str_2);
static const DEF_STRDESC((u"0002"), str_3);
/* Language Descriptor */
static const uint8_t LangDescr[] = {
0x04, 0x03, 0x09, 0x04
};
const uint8_t * StringDescArray [DEF_MAX_STRINGS] = {
LangDescr,
reinterpret_cast<const uint8_t*> (&str_1),
reinterpret_cast<const uint8_t*> (&str_2),
reinterpret_cast<const uint8_t*> (&str_3),
};

66
V203/usb/usart/usb_desc.h Normal file
View file

@ -0,0 +1,66 @@
/********************************** (C) COPYRIGHT *******************************
* File Name : usb_desc.h
* Author : WCH
* Version : V1.0.0
* Date : 2022/08/20
* Description : header file of usb_desc.c
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#ifndef USER_USB_DESC_H_
#define USER_USB_DESC_H_
#include <stdint.h>
/******************************************************************************/
/* global define */
/* file version */
#define DEF_FILE_VERSION 0x01
/* usb device info define */
#define DEF_USB_VID 0x1A86
#define DEF_USB_PID 0xFE0C
/* USB device descriptor, device serial number(bcdDevice) */
#define DEF_IC_PRG_VER DEF_FILE_VERSION
/******************************************************************************/
/* usb device endpoint size define */
#define DEF_USBD_UEP0_SIZE 64 /* usb hs/fs device end-point 0 size */
/* FS */
#define DEF_USBD_FS_PACK_SIZE 64 /* usb fs device max bluk/int pack size */
#define DEF_USBD_FS_ISO_PACK_SIZE 1023 /* usb fs device max iso pack size */
/* LS */
#define DEF_USBD_LS_UEP0_SIZE 8 /* usb ls device end-point 0 size */
#define DEF_USBD_LS_PACK_SIZE 64 /* usb ls device max int pack size */
/* Pack size */
#define DEF_USBD_ENDP1_SIZE DEF_USBD_FS_PACK_SIZE
#define DEF_USBD_ENDP2_SIZE DEF_USBD_FS_PACK_SIZE
#define DEF_USBD_ENDP3_SIZE DEF_USBD_FS_PACK_SIZE
#define DEF_USBD_ENDP4_SIZE DEF_USBD_FS_PACK_SIZE
#define DEF_USBD_ENDP5_SIZE DEF_USBD_FS_PACK_SIZE
#define DEF_USBD_ENDP6_SIZE DEF_USBD_FS_PACK_SIZE
#define DEF_USBD_ENDP7_SIZE DEF_USBD_FS_PACK_SIZE
/******************************************************************************/
/* usb device Descriptor length, length of usb descriptors, if one descriptor not
* exists , set the length to 0 */
#define DEF_USBD_DEVICE_DESC_LEN ((uint8_t)MyDevDescr[0])
#define DEF_USBD_CONFIG_DESC_LEN ((uint16_t)MyCfgDescr[2] + (uint16_t)(MyCfgDescr[3] << 8))
#define DEF_USBD_REPORT_DESC_LEN 0
#define DEF_MAX_STRINGS (4)
/******************************************************************************/
/* external variables */
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
extern const uint8_t MyDevDescr[ ];
extern const uint8_t MyCfgDescr[ ];
extern const uint8_t * StringDescArray [DEF_MAX_STRINGS];
#ifdef __cplusplus
};
#endif // __cplusplus
#endif /* USER_USB_DESC_H_ */