From 8f6a80e2c756bb977af60f32d1ce3cf068a75798 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Mon, 2 Mar 2026 23:21:31 +0300 Subject: [PATCH] USART1 @ STM32G0B1 --- G0:G070,G0B1/blink/blink.bin | Bin 872 -> 888 bytes G0:G070,G0B1/blink/systick_blink.c | 2 + G0:G070,G0B1/g0b1/hardware.c | 30 +++++ G0:G070,G0B1/g0b1/hardware.h | 35 +++++ G0:G070,G0B1/g0b1/main.c | 66 ++++------ G0:G070,G0B1/g0b1/ringbuffer.c | 180 ++++++++++++++++++++++++++ G0:G070,G0B1/g0b1/ringbuffer.h | 36 ++++++ G0:G070,G0B1/g0b1/test.bin | Bin 1220 -> 3660 bytes G0:G070,G0B1/g0b1/test.creator.user | 2 +- G0:G070,G0B1/g0b1/test.files | 6 + G0:G070,G0B1/g0b1/usart.c | 194 ++++++++++++++++++++++++++++ G0:G070,G0B1/g0b1/usart.h | 42 ++++++ G0:G070,G0B1/inc/Fx/stm32g0.h | 41 +++++- G0:G070,G0B1/inc/Fx/vector.h | 3 + G0:G070,G0B1/inc/startup/vector.c | 3 + 15 files changed, 592 insertions(+), 48 deletions(-) create mode 100644 G0:G070,G0B1/g0b1/hardware.c create mode 100644 G0:G070,G0B1/g0b1/hardware.h create mode 100644 G0:G070,G0B1/g0b1/ringbuffer.c create mode 100644 G0:G070,G0B1/g0b1/ringbuffer.h create mode 100644 G0:G070,G0B1/g0b1/usart.c create mode 100644 G0:G070,G0B1/g0b1/usart.h diff --git a/G0:G070,G0B1/blink/blink.bin b/G0:G070,G0B1/blink/blink.bin index 48c48b0c1a941526e19f23c5143505e47b75291e..8b53eaaf13faf172b2f0cc024add9d34975e6600 100755 GIT binary patch delta 520 zcmYk3F=!KE6vy9pca1fHVtO5#ODM@*C6q4GvqH&Wb7#J^_i3;dY1&&}62&iZQA)ur zH|ruj1WDW+<0i#HhjXmccXjI^b&B93E|yZPcR@;*$K&yTzu$xRBXOK~dHV2~4C4Th zk4j#0R8CA4pHu@H&wlNFqZ_nFD|CnLtZMvdFS)Ftr)T)ClD5Xh(^ct+EWw;C0y@pF zSjVBKci6g=;Ys^{PKt8jBfmk^E;{a5_&8;MZFxGfU5FE#?E{EO?gdi49h2^}x^p;k ztt;oW*c#rwkR4gYXQSryA`Z*D&7qvS2K3)i(oit{#1sbfAYUeIgPSN;Ku7@(*C delta 548 zcmYk4!E4h{9LImjnT)P+?&mtV>F<1Iry3~NLvs62c!XS zo@71Q-1O){PzE9%@+Tw;B0WfM^&)s2WiGxHkzL+{_xrxj_xt$1)6&n<%d?{ZfdBw1 zTU6e01I}>ia3{va+3%Ad_z~X2+jvNZt!j{+l+7v)wUyv_Y1tBzr!79?ZbKa{KbIO=;dZ9MOB^6|M!(Eh&jt zEypJIu1od). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hardware.h" + +void gpio_setup(){ + RCC->IOPENR = RCC_IOPENR_GPIOCEN | RCC_IOPENR_GPIOBEN; + // set PC8 as opendrain output, PC0 is pullup input, other as default (AIN) + GPIOC->MODER = (0xffffffff & ~(GPIO_MODER_MODE6 | GPIO_MODER_MODE13)) | GPIO_MODER_MODER6_O; // GPIO_MODER_MODER13_I == 0 + GPIOC->PUPDR = GPIO_PUPDR13_PD; // pull down + // USART1: PB6 - Tx (AF0), PB7 - Rx (AF0) + GPIOB->MODER = MODER_AF(6) | MODER_AF(7); + GPIOB->AFR[0] = 0; + // RCC->CCIPR = 0; // default -> sysclk/pclk source +} diff --git a/G0:G070,G0B1/g0b1/hardware.h b/G0:G070,G0B1/g0b1/hardware.h new file mode 100644 index 0000000..83fe299 --- /dev/null +++ b/G0:G070,G0B1/g0b1/hardware.h @@ -0,0 +1,35 @@ +/* + * This file is part of the test project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "stm32g0.h" + +// KEY (intpullup->0) - PC13 +// LED - PC6 +#define KEY_PORT GPIOC +#define KEY_PIN (1<<13) +#define LED_PORT GPIOC +#define LED_PIN (1<<6) +#define KEY_PRESSED() (pin_read(KEY_PORT, KEY_PIN) == 1) +#define LED_ON() do{pin_set(LED_PORT, LED_PIN);}while(0) +#define LED_OFF() do{pin_clear(LED_PORT, LED_PIN);}while(0) +#define LED_TOGGLE() do{pin_toggle(LED_PORT, LED_PIN);}while(0) + +extern volatile uint32_t Tms; +void gpio_setup(); diff --git a/G0:G070,G0B1/g0b1/main.c b/G0:G070,G0B1/g0b1/main.c index ecc9038..30df22f 100644 --- a/G0:G070,G0B1/g0b1/main.c +++ b/G0:G070,G0B1/g0b1/main.c @@ -17,70 +17,50 @@ */ #include "stm32g0.h" +#include "hardware.h" +#include "usart.h" -#define KEY_PORT GPIOC -#define KEY_PIN (1<<13) -#define LED_PORT GPIOC -#define LED_PIN (1<<6) -#define KEY_PRESSED() (pin_read(KEY_PORT, KEY_PIN) == 1) -#define LED_ON() do{pin_set(LED_PORT, LED_PIN);}while(0) -#define LED_OFF() do{pin_clear(LED_PORT, LED_PIN);}while(0) - -// KEY (intpullup->0) - PC13 -// LED - PC6 - -static volatile uint32_t blink_ctr = 0; +volatile uint32_t Tms = 0; // milliseconds /* Called when systick fires */ void sys_tick_handler(void){ - ++blink_ctr; + ++Tms; } /* * Set up timer to fire every x milliseconds */ static void systick_setup(uint32_t xms){ // xms < 2098!!! - blink_ctr = 0; + Tms = 0; static uint32_t curms = 0; if(curms == xms) return; - // 8MHz - HCLK/8 // this function also clears counter so it starts right away - SysTick_Config(8000 * xms); // arg should be < 0xffffff, so ms should be < 2098 + SysTick_Config(SysFreq / 8000 * xms); // arg should be < 0xffffff, so ms should be < 2098 curms = xms; } -static void gpio_setup(void){ - RCC->IOPENR = RCC_IOPENR_GPIOCEN; // enable PC - // set PC8 as opendrain output, PC0 is pullup input, other as default (AIN) - GPIOC->MODER = (0xffffffff & ~(GPIO_MODER_MODE6 | GPIO_MODER_MODE13)) | GPIO_MODER_MODER6_O; // GPIO_MODER_MODER13_I == 0 - GPIOC->PUPDR = GPIO_PUPDR13_PD; // pull down -} - -static const uint32_t L[] = {125,100,125,100,125,200, 350,100,350,100,350,200, 125,100,125,100,125, 1000}; - int main(void){ StartHSE(); gpio_setup(); - systick_setup(500); + systick_setup(1); // run each 1ms + usart_setup(115200); uint32_t M = 0; - int pressed = 0; + //int pressed = 0; /* Do nothing in main loop */ - while (1){ - if(KEY_PRESSED()){ // key pressed - 'sos' - pressed = 1; - systick_setup(L[M]); - if(M & 1) LED_OFF(); - else LED_ON(); - if(++M == 18) M = 0; - while(blink_ctr == 0); - }else{ // key not pressed - blink with period of 1s - if(pressed){ - M = 0; - pressed = 0; - systick_setup(500); - } - if(blink_ctr & 1) LED_ON(); - else LED_OFF(); + while(1){ + if(Tms - M > 499){ + LED_TOGGLE(); + M = Tms; + } + //if(KEY_PRESSED()){ // key pressed - 'sos' + USART_flags_t f = usart_process(); + if(f.rxovrfl) usart_sendstr("Rx overflow!\n"); + if(f.txerr) usart_sendstr("Tx error!\n"); + char *got = usart_getline(); + if(got){ + usart_sendstr("You sent:\n"); + usart_sendstr(got); + usart_sendstr("\n=======================\n"); } } } diff --git a/G0:G070,G0B1/g0b1/ringbuffer.c b/G0:G070,G0B1/g0b1/ringbuffer.c new file mode 100644 index 0000000..ebe722a --- /dev/null +++ b/G0:G070,G0B1/g0b1/ringbuffer.c @@ -0,0 +1,180 @@ +/* + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "ringbuffer.h" + +static int datalen(ringbuffer *b){ + if(b->tail >= b->head) return (b->tail - b->head); + else return (b->length - b->head + b->tail); +} + +// stored data length +int RB_datalen(ringbuffer *b){ + if(!b || b->busy) return -1; + b->busy = 1; + int l = datalen(b); + b->busy = 0; + return l; +} + +static int hasbyte(ringbuffer *b, uint8_t byte){ + if(!b || b->head == b->tail) return -1; // no data in buffer + int startidx = b->head; + if(b->head > b->tail){ // + for(int found = b->head; found < b->length; ++found) + if(b->data[found] == byte) return found; + startidx = 0; + } + for(int found = startidx; found < b->tail; ++found) + if(b->data[found] == byte) return found; + return -1; +} + +/** + * @brief RB_hasbyte - check if buffer has given byte stored + * @param b - buffer + * @param byte - byte to find + * @return index if found, -1 if none or busy + */ +int RB_hasbyte(ringbuffer *b, uint8_t byte){ + if(!b || b->busy) return -1; + b->busy = 1; + int ret = hasbyte(b, byte); + b->busy = 0; + return ret; +} + +// increment head or tail +TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){ + *what += n; + if(*what >= b->length) *what -= b->length; +} + +static int read(ringbuffer *b, uint8_t *s, int len){ + int l = datalen(b); + if(!l) return 0; + if(l > len) l = len; + int _1st = b->length - b->head; + if(_1st > l) _1st = l; + if(_1st > len) _1st = len; + memcpy(s, b->data + b->head, _1st); + if(_1st < len && l > _1st){ + memcpy(s+_1st, b->data, l - _1st); + incr(b, &b->head, l); + return l; + } + incr(b, &b->head, _1st); + return _1st; +} + +/** + * @brief RB_read - read data from ringbuffer + * @param b - buffer + * @param s - array to write data + * @param len - max len of `s` + * @return bytes read or -1 if busy + */ +int RB_read(ringbuffer *b, uint8_t *s, int len){ + if(!b || b->busy || !s || len < 1) return -1; + b->busy = 1; + int r = read(b, s, len); + b->busy = 0; + return r; +} + +// length of data from current position to `byte` (including byte) +static int lento(ringbuffer *b, uint8_t byte){ + int idx = hasbyte(b, byte); + if(idx < 0) return 0; + int partlen = idx + 1 - b->head; + // now calculate length of new data portion + if(idx < b->head) partlen += b->length; + return partlen; +} + +static int readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){ + int partlen = lento(b, byte); + if(!partlen) return 0; + if(partlen > len) return -1; + return read(b, s, partlen); +} + +/** + * @brief RB_readto fill array `s` with data until byte `byte` (with it) + * @param b - ringbuffer + * @param byte - check byte + * @param s - buffer to write data + * @param len - length of `s` + * @return amount of bytes written (negative, if lenbusy || !s || len < 1) return -1; + b->busy = 1; + int n = readto(b, byte, s, len); + b->busy = 0; + return n; +} + +int RB_datalento(ringbuffer *b, uint8_t byte){ + if(!b || b->busy) return -1; + b->busy = 1; + int n = lento(b, byte); + b->busy = 0; + return n; +} + +// if l < rest of buffer, truncate and return actually written bytes +static int write(ringbuffer *b, const uint8_t *str, int l){ + int r = b->length - 1 - datalen(b); // rest length + if(r < 1) return 0; + if(l > r) l = r; + int _1st = b->length - b->tail; + if(_1st > l) _1st = l; + memcpy(b->data + b->tail, str, _1st); + if(_1st < l){ // add another piece from start + memcpy(b->data, str+_1st, l-_1st); + } + incr(b, &b->tail, l); + return l; +} + +/** + * @brief RB_write - write some data to ringbuffer + * @param b - buffer + * @param str - data + * @param l - length + * @return amount of bytes written or -1 if busy + */ +int RB_write(ringbuffer *b, const uint8_t *str, int l){ + if(!b || b->busy || !str || l < 1) return -1; + b->busy = 1; + int w = write(b, str, l); + b->busy = 0; + return w; +} + +// just delete all information in buffer `b` +int RB_clearbuf(ringbuffer *b){ + if(!b || b->busy) return -1; + b->busy = 1; + b->head = 0; + b->tail = 0; + b->busy = 0; + return 1; +} diff --git a/G0:G070,G0B1/g0b1/ringbuffer.h b/G0:G070,G0B1/g0b1/ringbuffer.h new file mode 100644 index 0000000..f9e1e64 --- /dev/null +++ b/G0:G070,G0B1/g0b1/ringbuffer.h @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +typedef struct{ + uint8_t *data; // data buffer + const int length; // its length + int head; // head index + int tail; // tail index + volatile int busy; // == TRUE if buffer is busy now +} ringbuffer; + +int RB_read(ringbuffer *b, uint8_t *s, int len); +int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len); +int RB_hasbyte(ringbuffer *b, uint8_t byte); +int RB_write(ringbuffer *b, const uint8_t *str, int l); +int RB_datalen(ringbuffer *b); +int RB_datalento(ringbuffer *b, uint8_t byte); +int RB_clearbuf(ringbuffer *b); diff --git a/G0:G070,G0B1/g0b1/test.bin b/G0:G070,G0B1/g0b1/test.bin index 6ba82a51f89ec5c6fe39785b64a3a147924102c8..c153808acae1b42213f45a564990f6b4a6533911 100755 GIT binary patch literal 3660 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 diff --git a/G0:G070,G0B1/g0b1/test.creator.user b/G0:G070,G0B1/g0b1/test.creator.user index e56686e..d0086c0 100644 --- a/G0:G070,G0B1/g0b1/test.creator.user +++ b/G0:G070,G0B1/g0b1/test.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/G0:G070,G0B1/g0b1/test.files b/G0:G070,G0B1/g0b1/test.files index 59ab707..c64d491 100644 --- a/G0:G070,G0B1/g0b1/test.files +++ b/G0:G070,G0B1/g0b1/test.files @@ -1,2 +1,8 @@ +hardware.c +hardware.h main.c +ringbuffer.c +ringbuffer.h systick_blink.c +usart.c +usart.h diff --git a/G0:G070,G0B1/g0b1/usart.c b/G0:G070,G0B1/g0b1/usart.c new file mode 100644 index 0000000..d2edba0 --- /dev/null +++ b/G0:G070,G0B1/g0b1/usart.c @@ -0,0 +1,194 @@ +/* + * This file is part of the test project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "hardware.h" // Tms +#include "ringbuffer.h" +#include "usart.h" + +// We do not use a ring buffer here because we expect incoming information +// to flow slowly from the terminal. + +// USART-depending part -------> +// select USART and its DMA channels +#define USARTx USART1 +#define USARTxAPB APBENR2 +#define USARTxEN RCC_APBENR2_USART1EN +#define USART_APBEN RCC_APB1 +// DMAMUX channels: 50 - USART1Rx, 51 - USART1Tx +#define DMAMUXRXN (50) +#define DMAMUXTXN (51) +// DMA channels: 2 (1 in MUX) - Rx, 3 (2 in MUX) - Tx; TC and error flags +// use DMA ch2/3 because they both have single IRQ +#define DMAx DMA1 +#define DMAxEN RCC_AHBENR_DMA1EN +#define DMACHRX DMA1_Channel2 +#define DMARXTCF DMA_ISR_TCIF2 +#define DMARXEF DMA_ISR_TEIF2 +#define DMACHTX DMA1_Channel3 +#define DMATXTCF DMA_ISR_TCIF3 +#define DMATXEF DMA_ISR_TEIF3 +#define DMAMUXRX DMAMUX1_Channel1 +#define DMAMUXTX DMAMUX1_Channel2 +#define USARTIRQn USART1_IRQn +#define DMAIRQ DMA1_Channel2_3_IRQn +// interrupt aliases +static void usart_isr(); +static void dma_isr(); +void usart1_isr() __attribute__ ((alias ("usart_isr"))); +void dma1_channel2_3_isr() __attribute__ ((alias ("dma_isr"))); +// <-------- USART-depending part + +// RX/TX DMA->CCR without EN flag +#define DMARXCCR (DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_TEIE) +#define DMATXCCR (DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE | DMA_CCR_TEIE) + +static volatile uint8_t txrdy = 1, rxrdy = 0; // transmission done, next line received +static volatile USART_flags_t curflags; // current flags (cleared in `usart_process`) +static volatile uint8_t rbufno = 0; // current buf number +static uint8_t rbuf[USARTRXBUFSZ][2]; +static uint8_t txbuf[USARTTXBUFSZ]; // for ringbuffer +static ringbuffer TxRB = {.data = txbuf, .length = USARTTXBUFSZ}; + +char *usart_getline(){ + if(!rxrdy) return NULL; + rxrdy = 0; // clear ready flag + return (char*)rbuf[!rbufno]; // current buffer is in filling stage, return old - filled - buffer +} + +#define USART_BRR(speed) ((SysFreq + speed/2) / speed) + +void usart_setup(uint32_t speed){ + RCC->AHBENR |= DMAxEN; // enable DMA + // enable USART clocking + RCC->USARTxAPB |= USARTxEN; + // baudrate + USARTx->BRR = USART_BRR(speed); + // eol character: '/n' + USARTx->CR2 = USART_CR2_ADD_VAL('\n'); + // enable DMA transmission + USARTx->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; + // set up DMA channels + // Tx channel: mem++, mem->periph, 8bit, compl.&err. irq + DMACHTX->CCR = DMATXCCR; + DMACHTX->CPAR = (uint32_t) &USARTx->TDR; // peripherial address + // Rx channel: mem++, periph->mem, 8bit, compl.&err. irq + DMACHRX->CCR = DMARXCCR; + DMACHRX->CPAR = (uint32_t) &USARTx->RDR; // peripherial address + DMACHRX->CNDTR = USARTRXBUFSZ; + DMACHRX->CMAR = (uint32_t)&rbuf[rbufno]; + // set up DMAMUX channels + // enumeration of DMAMUX starts from 0 (DMA - from 1)! + DMAMUXRX->CCR = DMAMUXRXN; + DMAMUXTX->CCR = DMAMUXTXN; + // charmatch interrupt, enable transmitter and receiver, enable usart + USARTx->CR1 = USART_CR1_CMIE | USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; + USARTx->ICR = 0xffffffff; // clear all flags + DMACHRX->CCR = DMARXCCR | DMA_CCR_EN; // start receiving right now + NVIC_EnableIRQ(USARTIRQn); + NVIC_EnableIRQ(DMAIRQ); +} + +/** + * @brief usart_sendbuf - send next data portion + * @return TRUE if sent something + */ +static int usart_sendbuf(){ + static uint8_t dmatxbuf[USARTTXDMABUFSZ]; + if(!txrdy) return FALSE; + int rd = RB_read(&TxRB, dmatxbuf, USARTTXDMABUFSZ); + if(rd < 1) return FALSE; // nothing to write or busy + // set up DMA + DMACHTX->CCR = DMATXCCR; + DMACHTX->CMAR = (uint32_t) dmatxbuf; + DMACHTX->CNDTR = rd; + USARTx->ICR = USART_ICR_TCCF; // clear TC flag + txrdy = 0; + // activate DMA + DMACHTX->CCR = DMATXCCR | DMA_CCR_EN; + return TRUE; +} + +int usart_send(const char *str, int len){ + if(!str || len < 1) return 0; + uint32_t t = Tms; + int sent = 0; + do{ + IWDG->KR = IWDG_REFRESH; + int put = RB_write(&TxRB, (uint8_t*)str, len); + if(put < 0) continue; // busy + else if(put == 0) usart_sendbuf(); // no place + else{ + len -= put; + sent += put; + str += put; + } + }while(len && (Tms - t) < USARTBLKTMOUT); // not more than `block` ms! + return sent; +} + +int usart_sendstr(const char *str){ + int l = strlen(str); + return usart_send(str, l); +} + +// return current flags +USART_flags_t usart_process(){ + static uint32_t Tlast = 0; + USART_flags_t flags = curflags; + curflags.all = 0; + if(RB_datalento(&TxRB, '\n') > 1 || Tms - Tlast >= USARTSENDTMOUT){ // send buffer as we found '\n' or each 10ms + if(usart_sendbuf()) Tlast = Tms; + } + return flags; +} + +// interrupt by '\n' +static void usart_isr(){ + if(USARTx->ISR & USART_ISR_CMF){ // got '\n' @ USARTx + DMACHRX->CCR = DMARXCCR; + rxrdy = 1; + int l = USARTRXBUFSZ - DMACHRX->CNDTR - 1; // strlen without '\n' + rbuf[rbufno][l] = 0; // throw out '\n' + rbufno = !rbufno; // prepare next buffer to receive + // reload DMA Rx with next buffer + DMACHRX->CMAR = (uint32_t) rbuf[rbufno]; + DMACHRX->CNDTR = USARTRXBUFSZ; + DMACHRX->CCR = DMARXCCR | DMA_CCR_EN; + } + USARTx->ICR = 0xffffffff; // clear all flags +} + +// ch2 - Tx, ch3 - Rx +static void dma_isr(){ + volatile uint32_t isr = DMAx->ISR; + if(isr & DMATXTCF) txrdy = 1; + if(isr & DMATXEF) curflags.txerr = 1; + if(isr & (DMARXTCF | DMARXEF)){ // receive complete or error -> buffer overflow + if(rbuf[rbufno][USARTRXBUFSZ-1] != '\n'){ // last symbol is not a newline + curflags.rxovrfl = 1; + DMACHRX->CCR = DMARXCCR; // stop to reload + DMACHRX->CNDTR = USARTRXBUFSZ; + DMACHRX->CMAR = (uint32_t) rbuf[rbufno]; + DMACHRX->CCR = DMARXCCR | DMA_CCR_EN; + } + } + DMAx->IFCR = 0xffffffff; // clear all flags +} diff --git a/G0:G070,G0B1/g0b1/usart.h b/G0:G070,G0B1/g0b1/usart.h new file mode 100644 index 0000000..9c5d69b --- /dev/null +++ b/G0:G070,G0B1/g0b1/usart.h @@ -0,0 +1,42 @@ +/* + * This file is part of the test project. + * Copyright 2026 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#define USARTTXBUFSZ (1024) +#define USARTRXBUFSZ (128) +#define USARTTXDMABUFSZ (256) + +// blocking timeout - not more than 5ms +#define USARTBLKTMOUT (5) +// send buffer each 10ms +#define USARTSENDTMOUT (10) + +typedef union{ + struct{ + uint8_t txerr : 1; // transmit error + uint8_t rxovrfl : 1; // receive buffer overflow + }; + uint8_t all; +} USART_flags_t; + +void usart_setup(uint32_t speed); +int usart_send(const char *str, int len); +char *usart_getline(); +int usart_sendstr(const char *str); +USART_flags_t usart_process(); diff --git a/G0:G070,G0B1/inc/Fx/stm32g0.h b/G0:G070,G0B1/inc/Fx/stm32g0.h index 5b05384..19007de 100644 --- a/G0:G070,G0B1/inc/Fx/stm32g0.h +++ b/G0:G070,G0B1/inc/Fx/stm32g0.h @@ -80,12 +80,17 @@ TRUE_INLINE void StartHSEHSI(int isHSE){ WAITWHILE(PWR->SR2 & PWR_SR2_VOSF); if(isHSE){ RCC->PLLCFGR = ((PLLR-1)<<29) | ((PLLQ-1)<<25) | ((PLLP-1)<<17) | (PLLN<<8) | ((PLLM-1)<<4) - | RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLQEN /* | RCC_PLLCFGR_PLLPEN */ + | RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLPEN +#ifdef STM32G0B1xx + | RCC_PLLCFGR_PLLQEN +#endif | RCC_PLLCFGR_PLLSRC_HSE; }else{ // 64MHz from HSI16 - RCC->PLLCFGR = (8<<8) | (1<<4) - // enable P if need - | RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLQEN /* | RCC_PLLCFGR_PLLPEN */ + RCC->PLLCFGR = ((PLLR-1)<<29) | ((PLLQ-1)<<25) | ((PLLP-1)<<17) | (8<<8) | (1<<4) + | RCC_PLLCFGR_PLLREN | RCC_PLLCFGR_PLLPEN +#ifdef STM32G0B1xx + | RCC_PLLCFGR_PLLQEN +#endif | RCC_PLLCFGR_PLLSRC_HSI; } RCC->CR |= RCC_CR_PLLON; @@ -93,6 +98,7 @@ TRUE_INLINE void StartHSEHSI(int isHSE){ FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_LATENCY_2; // FLASH_ACR_LATENCY_2 for 64MHz // set sysclk switch to pll, setup AHB/APB RCC->CFGR = RCC_CFGR_SW_1 | PPRE << 12 | HPRE << 8; + SysFreq = 64000000; } #define StartHSE() do{StartHSEHSI(1);}while(0) @@ -224,6 +230,33 @@ TRUE_INLINE void StartHSEHSI(int isHSE){ #define GPIO_OSPEEDR15_MED ((uint32_t)(1<<30)) #define GPIO_OSPEEDR15_HIGH ((uint32_t)(3<<30)) +// clear MODER: ~GPIO_MODER_MODERXX_Msk, you should AND these +#define MODER_CLR(n) (~(3<<(n*2))) +// _AI - analog inpt, _O - general output, _AF - alternate function +// these should be OR'ed +#define MODER_I(n) (0) +#define MODER_O(n) (1<<(n*2)) +#define MODER_AF(n) (2<<(n*2)) +#define MODER_AI(n) (3<<(n*2)) +// OSPEED: low, medium, high +#define OSPEED_CLR(n) (~(3<<(n*2))) +#define OSPEED_VLO(n) (0) +#define OSPEED_LO(n) (1<<(n*2)) +#define OSPEED_MED(n) (2<<(n*2)) +#define OSPEED_HI(n) (3<<(n*2)) +// PUPD: pull up/down +#define PUPD_CLR(n) (~(3<<(n*2))) +#define PUPD_PU(n) (1<<(n*2)) +#define PUPD_PD(n) (2<<(n*2)) +// OTYPER: bit==1 for OD +#define OTYPER_PP(n) 0 +#define OTYPER_OD(n) (1< 7) pin -= 8; + return (afr << (pin * 4)); +} /****************** FLASH Keys **********************************************/ diff --git a/G0:G070,G0B1/inc/Fx/vector.h b/G0:G070,G0B1/inc/Fx/vector.h index 96e1cb6..4cc0973 100644 --- a/G0:G070,G0B1/inc/Fx/vector.h +++ b/G0:G070,G0B1/inc/Fx/vector.h @@ -26,6 +26,9 @@ #define WEAK __attribute__((weak)) #endif +#include +extern uint32_t SysFreq; + void WEAK reset_handler(void); void WEAK nmi_handler(void); void WEAK hard_fault_handler(void); diff --git a/G0:G070,G0B1/inc/startup/vector.c b/G0:G070,G0B1/inc/startup/vector.c index db9c9da..b0bbffb 100644 --- a/G0:G070,G0B1/inc/startup/vector.c +++ b/G0:G070,G0B1/inc/startup/vector.c @@ -19,6 +19,8 @@ */ #include "vector.h" +uint32_t SysFreq = 8000000; + typedef void (*vector_table_entry_t)(void); typedef void (*funcp_t) (void); @@ -39,6 +41,7 @@ void null_handler(void); #define NVIC_IRQ_COUNT 32 #if defined(STM32G070xx) +#define IRQ_HANDLERS \ [WWDG_IRQn] = wwdg_isr, \ [RTC_TAMP_IRQn] = rtc_isr, \ [FLASH_IRQn] = flash_isr, \