// // #include <stdint.h>
// // #include "stm32c0xx.h" // Device header for STM32C0 series
// // class Lcd8Bit {
// // public:
// // void GPIO_config()
// // {
// // // Enable GPIOA and GPIOB clocks
// // RCC->IOPENR |= (1 << 0) | (1 << 1);
// // // Configure PA0, PA1 as output (RS, E)
// // GPIOA->MODER &= ~((3 << 0) | (3 << 2));
// // GPIOA->MODER |= ((1 << 0) | (1 << 2));
// // GPIOA->OSPEEDR |= ((2 << 0) | (2 << 2));
// // GPIOA->OTYPER &= ~((1 << 0) | (1 << 1));
// // GPIOA->PUPDR &= ~((3 << 0) | (3 << 2));
// // // Configure PB0..PB7 as output (D0..D7)
// // GPIOB->MODER &= ~0x0000FFFF;
// // GPIOB->MODER |= 0x00005555;
// // GPIOB->OSPEEDR |= 0x0000AAAA;
// // GPIOB->OTYPER &= ~0xFF;
// // GPIOB->PUPDR &= ~0x0000FFFF;
// // // Reset control and data lines
// // GPIOA->ODR &= ~((1 << 0) | (1 << 1));
// // GPIOB->ODR &= ~0xFF;
// // }
// // void delay(unsigned int count)
// // {
// // while (count--) { asm("nop"); }
// // }
// // void delay_ms(unsigned int ms)
// // {
// // for (unsigned int i = 0; i < ms; i++)
// // {
// // delay(16000); // ~1ms at 16MHz
// // }
// // }
// // void pulse_enable()
// // {
// // GPIOA->ODR |= (1 << 1);
// // delay(200);
// // GPIOA->ODR &= ~(1 << 1);
// // delay(200);
// // }
// // void lcd_write_bus(uint8_t value)
// // {
// // GPIOB->ODR = (GPIOB->ODR & ~0xFF) | value;
// // }
// // void lcd_command(uint8_t cmd)
// // {
// // GPIOA->ODR &= ~(1 << 0); // RS = 0
// // lcd_write_bus(cmd);
// // pulse_enable();
// // if (cmd == 0x01 || cmd == 0x02)
// // delay_ms(2);
// // else
// // delay(4000);
// // }
// // void lcd_data(uint8_t data)
// // {
// // GPIOA->ODR |= (1 << 0); // RS = 1
// // lcd_write_bus(data);
// // pulse_enable();
// // delay(4000);
// // }
// // void lcd_init()
// // {
// // GPIO_config();
// // delay_ms(40);
// // lcd_command(0x38); // 8-bit, 2 lines
// // delay_ms(5);
// // lcd_command(0x0C); // Display ON, cursor OFF
// // delay_ms(1);
// // lcd_command(0x01); // Clear
// // delay_ms(2);
// // lcd_command(0x06); // Entry mode
// // delay_ms(1);
// // }
// // void lcd_goto(uint8_t row, uint8_t col)
// // {
// // uint8_t addr = (row == 0) ? col : (0x40 + col);
// // lcd_command(0x80 | addr);
// // }
// // void lcd_puts(const char* str)
// // {
// // while (*str)
// // {
// // lcd_data((uint8_t)*str++);
// // }
// // }
// // };
// // class Uart1 {
// // public:
// // void gpio_uart_config()
// // {
// // // Enable GPIOA clock
// // RCC->IOPENR |= (1 << 0);
// // // PA9 (TX) and PA10 (RX) to Alternate Function mode (10)
// // GPIOA->MODER &= ~((3 << (9 * 2)) | (3 << (10 * 2)));
// // GPIOA->MODER |= ((2 << (9 * 2)) | (2 << (10 * 2)));
// // // Push-pull, high speed, optional pull-up on RX
// // GPIOA->OTYPER &= ~((1 << 9) | (1 << 10));
// // GPIOA->OSPEEDR |= ((3 << (9 * 2)) | (3 << (10 * 2)));
// // GPIOA->PUPDR &= ~((3 << (9 * 2)) | (3 << (10 * 2)));
// // GPIOA->PUPDR |= (1 << (10 * 2)); // RX pull-up (optional)
// // // AF1 for USART1 on PA9/PA10
// // GPIOA->AFR[1] &= ~((0xF << ((9 - 8) * 4)) | (0xF << ((10 - 8) * 4)));
// // GPIOA->AFR[1] |= ((1 << ((9 - 8) * 4)) | (1 << ((10 - 8) * 4)));
// // }
// // void uart_init_9600()
// // {
// // // Enable USART1 clock (APB)
// // RCC->APBENR2 |= (1 << 14); // USART1EN
// // // Disable USART before config
// // USART1->CR1 &= ~(1 << 0); // UE = 0
// // // Baud = 9600 @ 16 MHz: BRR ≈ 0x0683
// // USART1->BRR = 0x0683;
// // // Enable transmitter and receiver
// // USART1->CR1 |= (1 << 2) | (1 << 3); // RE | TE
// // // Enable USART
// // USART1->CR1 |= (1 << 0); // UE = 1
// // // Clear any pending errors (ORE, NE, FE) and RXNE
// // // Read RDR then clear flags in ICR (if available on your device variant).
// // volatile uint32_t dummy = USART1->RDR;
// // (void)dummy;
// // // If your stm32c0xx.h exposes ICR bits, you can uncomment:
// // // USART1->ICR |= (1 << 3) | (1 << 2) | (1 << 1) | (1 << 5); // ORECF, NCECF, FECF, RXNECF (device-dependent)
// // }
// // void send_byte(uint8_t b)
// // {
// // // Wait until TXE (bit 7) -> TDR empty
// // while ((USART1->ISR & (1 << 7)) == 0) { }
// // USART1->TDR = b;
// // // Wait for TC (bit 6) transmission complete (ensures byte shifted out)
// // while ((USART1->ISR & (1 << 6)) == 0) { }
// // }
// // uint8_t recv_byte_blocking()
// // {
// // // Wait until RXNE (bit 5)
// // while ((USART1->ISR & (1 << 5)) == 0) { }
// // return (uint8_t)(USART1->RDR & 0xFF);
// // }
// // };
// // int main()
// // {
// // Lcd8Bit lcd;
// // Uart1 uart;
// // lcd.lcd_init();
// // uart.gpio_uart_config();
// // uart.uart_init_9600();
// // // Clear LCD and set cursor at start
// // lcd.lcd_command(0x01);
// // lcd.delay_ms(2);
// // lcd.lcd_goto(0, 0);
// // // Loopback: jumper PA9 (TX) to PA10 (RX)
// // const char* msg = "UART msg";
// // const char* s = msg;
// // // Transmit and receive per character to avoid RX overrun
// // while (*s)
// // {
// // // Send one byte
// // uart.send_byte((uint8_t)*s);
// // // Immediately read the received byte
// // uint8_t c = uart.recv_byte_blocking();
// // // Show on LCD
// // lcd.lcd_data(c);
// // s++;
// // }
// // // Second line status
// // lcd.lcd_goto(1, 0);
// // lcd.lcd_puts("RX OK");
// // while (1)
// // {
// // // Idle
// // }
// // }
// #include <stdint.h>
// #include "stm32c0xx.h" // device header
// // -------------------- LCD class (8-bit, PA0=RS, PA1=E, PB[0..7]=D0..D7) --------------------
// class Lcd8Bit {
// public:
// void GPIO_config() {
// // Enable GPIOA/GPIOB clocks
// RCC->IOPENR |= (1 << 0) | (1 << 1);
// // PA0, PA1 as push-pull outputs
// GPIOA->MODER &= ~((3 << (0*2)) | (3 << (1*2)));
// GPIOA->MODER |= ((1 << (0*2)) | (1 << (1*2)));
// GPIOA->OSPEEDR |= ((2 << (0*2)) | (2 << (1*2)));
// GPIOA->OTYPER &= ~((1 << 0) | (1 << 1));
// GPIOA->PUPDR &= ~((3 << (0*2)) | (3 << (1*2)));
// // PB0..PB7 as push-pull outputs
// GPIOB->MODER &= ~0x0000FFFF;
// GPIOB->MODER |= 0x00005555;
// GPIOB->OSPEEDR |= 0x0000AAAA;
// GPIOB->OTYPER &= ~0x00FF;
// GPIOB->PUPDR &= ~0x0000FFFF;
// // Default low
// GPIOA->ODR &= ~((1 << 0) | (1 << 1));
// GPIOB->ODR &= ~0xFF;
// }
// void delay(unsigned int n){ while(n--){ asm("nop"); } }
// void delay_ms(unsigned int ms){ for(unsigned int i=0;i<ms;i++){ delay(16000); } }
// void pulse_enable(){ GPIOA->ODR |= (1<<1); delay(200); GPIOA->ODR &= ~(1<<1); delay(200); }
// void lcd_write_bus(uint8_t v){ GPIOB->ODR = (GPIOB->ODR & ~0xFF) | v; }
// void lcd_command(uint8_t c){
// GPIOA->ODR &= ~(1<<0); // RS=0
// lcd_write_bus(c);
// pulse_enable();
// if(c==0x01||c==0x02) delay_ms(2); // clear/home long
// else delay(4000);
// }
// void lcd_data(uint8_t d){
// GPIOA->ODR |= (1<<0); // RS=1
// lcd_write_bus(d);
// pulse_enable();
// delay(4000);
// }
// void lcd_init(){
// GPIO_config();
// delay_ms(40);
// lcd_command(0x38); // 8-bit, 2 lines, 5x8
// delay_ms(5);
// lcd_command(0x0C); // display on, cursor off
// delay_ms(1);
// lcd_command(0x01); // clear
// delay_ms(2);
// lcd_command(0x06); // entry mode: inc, no shift
// delay_ms(1);
// }
// void lcd_goto(uint8_t r,uint8_t c){ uint8_t a=(r==0)?c:(0x40+c); lcd_command(0x80|a); }
// void lcd_puts(const char* s){ while(*s){ lcd_data((uint8_t)*s++); } }
// };
// // -------------------- UART + DMA helper (USART1 on PA9/PA10) --------------------
// class Uart1Dma {
// public:
// // Choose DMA1 channels (1..3 on STM32C031)
// int tx_channel_index; // 1
// int rx_channel_index; // 2
// // DMAMUX request IDs (from RM0490 Table 49) -> as provided:
// // USART1_RX = 50, USART1_TX = 51
// int DMAMUX_TX_REQ_ID;
// int DMAMUX_RX_REQ_ID;
// Uart1Dma() {
// tx_channel_index = 1; // DMA1 Channel 1 for TX
// rx_channel_index = 2; // DMA1 Channel 2 for RX
// DMAMUX_TX_REQ_ID = 51; // USART1_TX request ID
// DMAMUX_RX_REQ_ID = 50; // USART1_RX request ID
// }
// void gpio_uart_config()
// {
// // Enable GPIOA clock
// RCC->IOPENR |= (1 << 0);
// // PA9 (TX), PA10 (RX) to AF1
// GPIOA->MODER &= ~((3 << (9*2)) | (3 << (10*2)));
// GPIOA->MODER |= ((2 << (9*2)) | (2 << (10*2)));
// GPIOA->OTYPER &= ~((1 << 9) | (1 << 10));
// GPIOA->OSPEEDR |= ((3 << (9*2)) | (3 << (10*2)));
// GPIOA->PUPDR &= ~((3 << (9*2)) | (3 << (10*2)));
// GPIOA->PUPDR |= (1 << (10*2)); // RX pull-up optional
// GPIOA->AFR[1] &= ~((0xF << ((9-8)*4)) | (0xF << ((10-8)*4)));
// GPIOA->AFR[1] |= ((1 << ((9-8)*4)) | (1 << ((10-8)*4))); // AF1
// }
// void uart_init_9600()
// {
// // Enable USART1 clock
// RCC->APBENR2 |= (1 << 14); // USART1EN (USART chapter + RCC) [2](https://bosch-my.sharepoint.com/personal/vha3cob_bosch_com/Documents/Microsoft%20Copilot%20Chat%20Files/STM32_Nucleo_C031c6.pdf)
// USART1->CR1 &= ~(1 << 0); // UE=0
// USART1->BRR = 0x0683; // 9600 @ ~16 MHz (oversampling by 16) [2](https://bosch-my.sharepoint.com/personal/vha3cob_bosch_com/Documents/Microsoft%20Copilot%20Chat%20Files/STM32_Nucleo_C031c6.pdf)
// USART1->CR1 |= (1 << 2) | (1 << 3); // RE | TE
// USART1->CR3 |= (1 << 7) | (1 << 6); // DMAT | DMAR (enable DMA for TX & RX) [2](https://bosch-my.sharepoint.com/personal/vha3cob_bosch_com/Documents/Microsoft%20Copilot%20Chat%20Files/STM32_Nucleo_C031c6.pdf)
// USART1->CR1 |= (1 << 0); // UE=1
// // Clear any pending data
// volatile uint32_t d = USART1->RDR; (void)d;
// }
// void dma_clock_enable()
// {
// // Enable DMA1 and DMAMUX clocks (RCC->AHBENR on STM32C0)
// RCC->AHBENR |= (1 << 0); // DMA1EN
// RCC->AHBENR |= (1 << 1); // DMAMUXEN
// }
// // Helper to get DMA1_ChannelX pointer by index (1..3 on STM32C031)
// DMA_Channel_TypeDef* dma_ch(int ch) {
// switch(ch){
// case 1: return DMA1_Channel1;
// case 2: return DMA1_Channel2;
// case 3: return DMA1_Channel3;
// default: return DMA1_Channel1;
// }
// }
// void dmamux_route_channel_to_request(int ch, int req_id)
// {
// // Map DMA1 channel N -> DMAMUX1 Channel (N-1), write DMAREQ_ID[5:0]
// // DMAMUX channel CxCR has field DMAREQ_ID[5:0]. [2](https://bosch-my.sharepoint.com/personal/vha3cob_bosch_com/Documents/Microsoft%20Copilot%20Chat%20Files/STM32_Nucleo_C031c6.pdf)
// if (ch == 1) {
// DMAMUX1_Channel0->CCR = (uint32_t)(req_id & 0x3F);
// } else if (ch == 2) {
// DMAMUX1_Channel1->CCR = (uint32_t)(req_id & 0x3F);
// } else { // ch == 3
// DMAMUX1_Channel2->CCR = (uint32_t)(req_id & 0x3F);
// }
// }
// void dma_config_tx(const char* src, int len)
// {
// // Route TX channel to USART1_TX
// dmamux_route_channel_to_request(tx_channel_index, DMAMUX_TX_REQ_ID);
// DMA_Channel_TypeDef* ch = dma_ch(tx_channel_index);
// // Disable channel before reconfig
// ch->CCR = 0;
// // Peripheral address: USART1->TDR (TX data)
// ch->CPAR = (uint32_t)&(USART1->TDR);
// // Memory address: source buffer
// ch->CMAR = (uint32_t)src;
// // Number of data items
// ch->CNDTR = (uint32_t)len;
// // Configure CCR:
// // DIR=1 (memory->peripheral), MINC=1, PINC=0, CIRC=0, MSIZE=0 (8-bit), PSIZE=0 (8-bit)
// // uint32_t cfg = 0;
// // cfg |= (1 << 4); // MINC
// // cfg |= (1 << 1); // DIR: memory->peripheral (DMA chapter) [1](https://bosch-my.sharepoint.com/personal/vha3cob_bosch_com/Documents/Microsoft%20Copilot%20Chat%20Files/STM32_Nucleo_C031c6.pdf)
// // ch->CCR = cfg;
// uint32_t cfg = 0;
// cfg |= (1 << 4); // DIR = 1 (mem->periph)
// cfg |= (1 << 7); // MINC = 1
// ch->CCR = cfg;
// }
// void dma_config_rx(char* dst, int len, int circular)
// {
// // Route RX channel to USART1_RX
// dmamux_route_channel_to_request(rx_channel_index, DMAMUX_RX_REQ_ID);
// DMA_Channel_TypeDef* ch = dma_ch(rx_channel_index);
// // Disable channel
// ch->CCR = 0;
// // Peripheral address: USART1->RDR (RX data)
// ch->CPAR = (uint32_t)&(USART1->RDR);
// // Memory address: destination buffer
// ch->CMAR = (uint32_t)dst;
// // Number of items
// ch->CNDTR = (uint32_t)len;
// // DIR=0 (peripheral->memory), MINC=1, PINC=0, CIRC optional
// // uint32_t cfg = 0;
// // cfg |= (1 << 4); // MINC
// // if (circular) cfg |= (1 << 5); // CIRC
// // ch->CCR = cfg;
// uint32_t cfg = 0;
// cfg |= (1 << 7); // MINC = 1
// if (circular) cfg |= (1 << 5); // CIRC
// ch->CCR = cfg;
// }
// void dma_start_tx()
// {
// dma_ch(tx_channel_index)->CCR |= (1 << 0); // EN
// }
// void dma_start_rx()
// {
// dma_ch(rx_channel_index)->CCR |= (1 << 0); // EN
// }
// int dma_tx_remaining()
// {
// return (int)(dma_ch(tx_channel_index)->CNDTR);
// }
// int dma_rx_remaining()
// {
// return (int)(dma_ch(rx_channel_index)->CNDTR);
// }
// };
// int main()
// {
// Lcd8Bit lcd;
// Uart1Dma udma;
// // Init LCD
// lcd.lcd_init();
// // Init UART GPIO & peripheral
// udma.gpio_uart_config();
// udma.uart_init_9600();
// // Enable DMA/DMAMUX clocks
// udma.dma_clock_enable();
// // TX data
// const char tx_msg[] = "UART msg";
// int tx_len = sizeof(tx_msg) - 1;
// // RX buffer
// char rx_buf[32];
// for (int i=0; i<32; i++) { rx_buf[i] = 0; }
// // Configure RX first
// udma.dma_config_rx(rx_buf, tx_len, 0);
// udma.dma_start_rx();
// // Configure TX
// udma.dma_config_tx(tx_msg, tx_len);
// udma.dma_start_tx();
// // Poll TX CNDTR to know when DMA finished
// while (udma.dma_tx_remaining() != 0) { /* busy wait */ }
// while (udma.dma_rx_remaining() != 0) { }
// // Small wait to ensure TDR -> shift register => line
// for (volatile int k=0; k<20000; k++) { asm("nop"); }
// // Null-terminate RX buffer
// rx_buf[tx_len] = '\0';
// // Display
// lcd.lcd_command(0x01);
// lcd.delay_ms(2);
// lcd.lcd_goto(0, 0);
// lcd.lcd_puts((const char*)rx_buf);
// lcd.lcd_goto(1, 0);
// lcd.lcd_puts("DMA UART RX");
// while (1) { }
// }
#include <stdint.h>
#include "stm32c0xx.h" // device header
// --------- CONFIG TWEAKS (try these if RX stays blank) ---------
#define DMAMUX_INDEX_STARTS_AT_ZERO 1 // 1: DMA Ch1->MUX Ch0, Ch2->MUX Ch1 (common). If RX DMA never fires, set to 0.
#define USART1_RX_REQ_ID 50 // DMAMUX request ID for USART1_RX (from RM Table 49 for your variant)
#define USART1_TX_REQ_ID 51 // DMAMUX request ID for USART1_TX (from RM Table 49 for your variant)
// ----------------------------------------------------------------
// -------------------- LCD class (8-bit, PA0=RS, PA1=E, PB[0..7]=D0..D7) --------------------
class Lcd8Bit {
public:
void GPIO_config() {
// Enable GPIOA/GPIOB clocks
RCC->IOPENR |= (1 << 0) | (1 << 1);
// PA0, PA1 as push-pull outputs
GPIOA->MODER &= ~((3 << (0*2)) | (3 << (1*2)));
GPIOA->MODER |= ((1 << (0*2)) | (1 << (1*2)));
GPIOA->OSPEEDR |= ((2 << (0*2)) | (2 << (1*2)));
GPIOA->OTYPER &= ~((1 << 0) | (1 << 1));
GPIOA->PUPDR &= ~((3 << (0*2)) | (3 << (1*2)));
// PB0..PB7 as push-pull outputs
GPIOB->MODER &= ~0x0000FFFF;
GPIOB->MODER |= 0x00005555;
GPIOB->OSPEEDR |= 0x0000AAAA;
GPIOB->OTYPER &= ~0x00FF;
GPIOB->PUPDR &= ~0x0000FFFF;
// Default low
GPIOA->ODR &= ~((1 << 0) | (1 << 1));
GPIOB->ODR &= ~0xFF;
}
void delay(unsigned int n){ while(n--){ asm("nop"); } }
void delay_ms(unsigned int ms){ for(unsigned int i=0;i<ms;i++){ delay(16000); } }
void pulse_enable(){ GPIOA->ODR |= (1<<1); delay(200); GPIOA->ODR &= ~(1<<1); delay(200); }
void lcd_write_bus(uint8_t v){ GPIOB->ODR = (GPIOB->ODR & ~0xFF) | v; }
void lcd_command(uint8_t c){
GPIOA->ODR &= ~(1<<0); // RS=0
lcd_write_bus(c);
pulse_enable();
if(c==0x01||c==0x02) delay_ms(2); // clear/home long
else delay(4000);
}
void lcd_data(uint8_t d){
GPIOA->ODR |= (1<<0); // RS=1
lcd_write_bus(d);
pulse_enable();
delay(4000);
}
void lcd_init(){
GPIO_config();
delay_ms(40);
lcd_command(0x38); // 8-bit, 2 lines, 5x8
delay_ms(5);
lcd_command(0x0C); // display on, cursor off
delay_ms(1);
lcd_command(0x01); // clear
delay_ms(2);
lcd_command(0x06); // entry mode: inc, no shift
delay_ms(1);
}
void lcd_goto(uint8_t r,uint8_t c){ uint8_t a=(r==0)?c:(0x40+c); lcd_command(0x80|a); }
void lcd_puts(const char* s){ while(*s){ lcd_data((uint8_t)*s++); } }
};
// -------------------- UART + DMA helper (USART1 on PA9/PA10) --------------------
class Uart1Dma {
public:
// DMA1 has 3 channels on STM32C031; we use CH1=TX, CH2=RX
int tx_channel_index; // 1
int rx_channel_index; // 2
// DMAMUX request IDs (adjust if Table 49 differs on your variant)
int DMAMUX_TX_REQ_ID;
int DMAMUX_RX_REQ_ID;
Uart1Dma() {
tx_channel_index = 1;
rx_channel_index = 2;
DMAMUX_TX_REQ_ID = USART1_TX_REQ_ID;
DMAMUX_RX_REQ_ID = USART1_RX_REQ_ID;
}
void gpio_uart_config()
{
// Enable GPIOA clock
RCC->IOPENR |= (1 << 0);
// PA9 (TX), PA10 (RX) to AF1
GPIOA->MODER &= ~((3 << (9*2)) | (3 << (10*2)));
GPIOA->MODER |= ((2 << (9*2)) | (2 << (10*2)));
GPIOA->OTYPER &= ~((1 << 9) | (1 << 10));
GPIOA->OSPEEDR |= ((3 << (9*2)) | (3 << (10*2)));
GPIOA->PUPDR &= ~((3 << (9*2)) | (3 << (10*2)));
GPIOA->PUPDR |= (1 << (10*2)); // RX pull-up optional
GPIOA->AFR[1] &= ~((0xF << ((9-8)*4)) | (0xF << ((10-8)*4)));
GPIOA->AFR[1] |= ((1 << ((9-8)*4)) | (1 << ((10-8)*4))); // AF1
}
void uart_init_9600()
{
// Enable USART1 clock (APB2)
RCC->APBENR2 |= (1 << 14); // USART1EN
USART1->CR1 &= ~(1 << 0); // UE=0
USART1->BRR = 0x0683; // 9600 @ ~16 MHz (oversampling by 16)
USART1->CR1 |= (1 << 2) | (1 << 3); // RE | TE
USART1->CR3 |= (1 << 7) | (1 << 6); // DMAT | DMAR
USART1->CR1 |= (1 << 0); // UE=1
volatile uint32_t d = USART1->RDR; (void)d; // clear RXFNE
}
void dma_clock_enable()
{
// Enable DMA1 and DMAMUX clocks (AHB)
RCC->AHBENR |= (1 << 0); // DMA1EN
RCC->AHBENR |= (1 << 1); // DMAMUXEN
}
// Helper to get DMA1_ChannelX pointer by index (1..3)
DMA_Channel_TypeDef* dma_ch(int ch) {
switch(ch){
case 1: return DMA1_Channel1;
case 2: return DMA1_Channel2;
case 3: return DMA1_Channel3;
default: return DMA1_Channel1;
}
}
void dmamux_route_channel_to_request(int ch, int req_id)
{
// Map DMA1 ChN -> DMAMUX1 Channel index depending on silicon instantiation.
// If your device uses Ch1->Mux0, set DMAMUX_INDEX_STARTS_AT_ZERO=1; else 0.
int mux_index = DMAMUX_INDEX_STARTS_AT_ZERO ? (ch - 1) : ch;
switch(mux_index){
case 0: DMAMUX1_Channel0->CCR = (uint32_t)(req_id & 0x3F); break;
case 1: DMAMUX1_Channel1->CCR = (uint32_t)(req_id & 0x3F); break;
case 2: DMAMUX1_Channel2->CCR = (uint32_t)(req_id & 0x3F); break;
default: DMAMUX1_Channel0->CCR = (uint32_t)(req_id & 0x3F); break;
}
}
void dma_config_tx(const char* src, int len)
{
// Route TX channel to USART1_TX
dmamux_route_channel_to_request(tx_channel_index, DMAMUX_TX_REQ_ID);
DMA_Channel_TypeDef* ch = dma_ch(tx_channel_index);
ch->CCR = 0; // disable
// Peripheral (dest) = USART1->TDR, Memory (src) = src
ch->CPAR = (uint32_t)&(USART1->TDR);
ch->CMAR = (uint32_t)src;
ch->CNDTR = (uint32_t)len;
// CCR: DIR=1 (bit4), MINC=1 (bit7), 8-bit sizes, no CIRC
uint32_t cfg = 0;
cfg |= (1 << 4); // DIR mem->periph
cfg |= (1 << 7); // MINC
ch->CCR = cfg;
}
void dma_config_rx(char* dst, int len, int circular)
{
// Route RX channel to USART1_RX
dmamux_route_channel_to_request(rx_channel_index, DMAMUX_RX_REQ_ID);
DMA_Channel_TypeDef* ch = dma_ch(rx_channel_index);
ch->CCR = 0; // disable
// Peripheral (src) = USART1->RDR, Memory (dest) = dst
ch->CPAR = (uint32_t)&(USART1->RDR);
ch->CMAR = (uint32_t)dst;
ch->CNDTR = (uint32_t)len;
// CCR: DIR=0 (periph->mem), MINC=1 (bit7), CIRC optional, 8-bit sizes
uint32_t cfg = 0;
cfg |= (1 << 7); // MINC
if (circular) cfg |= (1 << 5);// CIRC
ch->CCR = cfg;
}
void dma_start_tx(){ dma_ch(tx_channel_index)->CCR |= (1 << 0); } // EN
void dma_start_rx(){ dma_ch(rx_channel_index)->CCR |= (1 << 0); } // EN
int dma_tx_remaining(){ return (int)(dma_ch(tx_channel_index)->CNDTR); }
int dma_rx_remaining(){ return (int)(dma_ch(rx_channel_index)->CNDTR); }
};
int main()
{
Lcd8Bit lcd;
Uart1Dma udma;
// Init LCD
lcd.lcd_init();
// Init UART GPIO & peripheral (PA9=TX, PA10=RX)
udma.gpio_uart_config();
udma.uart_init_9600();
// -------- UART POLLING SELF-TEST (proves wiring & BRR) --------
// Temporarily disable DMA path
// USART1->CR3 &= ~((1<<7) | (1<<6)); // DMAT=0, DMAR=0
// // Send 'U' and expect to read back 'U'
// while (!(USART1->ISR & (1<<7))) { } // TXFNF
// USART1->TDR = 'U';
// while (!(USART1->ISR & (1<<5))) { } // RXFNE
// uint8_t r = (uint8_t)USART1->RDR;
// lcd.lcd_command(0x01); lcd.delay_ms(2);
// lcd.lcd_goto(0,0); lcd.lcd_puts("POLL=");
// lcd.lcd_data(r ? r : '?');
// lcd.lcd_goto(1,0); lcd.lcd_puts("OK->DMA...");
// lcd.delay_ms(500);
// Re-enable DMA path
USART1->CR3 |= (1<<7) | (1<<6); // DMAT | DMAR
// ---------------------------------------------------------------
// Enable DMA/DMAMUX clocks
udma.dma_clock_enable();
// TX payload (send from SRAM to avoid any doubt)
const char tx_msg_flash[] = "UART msg";
int tx_len = (int)sizeof(tx_msg_flash) - 1;
char tx_msg[16]; // SRAM mirror
for (int i=0; i<tx_len; i++) tx_msg[i] = tx_msg_flash[i];
// RX buffer
char rx_buf[32];
for (int i=0; i<32; i++) rx_buf[i] = 0;
// Configure RX first (to catch all bytes)
udma.dma_config_rx(rx_buf, tx_len, 0);
udma.dma_start_rx();
// Configure and start TX
udma.dma_config_tx(tx_msg, tx_len);
udma.dma_start_tx();
// ---- DMA PROOF: wait for TC flags & show first byte ----
// DMA1->ISR: Ch1 TCIF=bit1, Ch2 TCIF=bit5
while ((DMA1->ISR & (1<<1)) == 0) { } // TX TCIF1
while ((DMA1->ISR & (1<<5)) == 0) { } // RX TCIF2
lcd.lcd_command(0x01); lcd.delay_ms(2);
lcd.lcd_goto(0,0);
lcd.lcd_puts("TX_TC=");
lcd.lcd_data('0' + ((DMA1->ISR>>1)&1));
lcd.lcd_puts(" RX_TC=");
lcd.lcd_data('0' + ((DMA1->ISR>>5)&1));
lcd.lcd_goto(1,0);
lcd.lcd_puts("b0=");
char b0 = rx_buf[0];
lcd.lcd_data(b0 ? b0 : '?');
lcd.delay_ms(400);
// --------------------------------------------------------
// ---- LIVE, CHAR-BY-CHAR PRINT AS BYTES ARRIVE ----
lcd.lcd_command(0x01);
lcd.delay_ms(2);
lcd.lcd_goto(0, 0);
int last_remaining = udma.dma_rx_remaining(); // starts at tx_len
int printed = 0;
while (last_remaining != 0) {
int rem = udma.dma_rx_remaining();
if (rem < last_remaining) {
int newly_arrived = last_remaining - rem;
for (int j = 0; j < newly_arrived; j++) {
if ((printed % 32) == 16) lcd.lcd_goto(1, 0);
lcd.lcd_data((uint8_t)rx_buf[printed + j]);
}
printed += newly_arrived;
last_remaining = rem;
}
}
// Footer on line-2
lcd.lcd_goto(1, 0);
lcd.lcd_puts("DMA UART RX");
while (1) { }
}