Implement USART with bot RX and TX interrupts

This commit is contained in:
2024-09-02 01:03:59 +02:00
parent 2423e0f060
commit 19983fb613
17 changed files with 168 additions and 440 deletions

View File

@@ -30,7 +30,7 @@ AVD := avrdude
MKDIR := mkdir -p MKDIR := mkdir -p
RMR := rm -rf RMR := rm -rf
CPPFLAGS := -DF_CPU=$(FREQ) CPPFLAGS := -DF_CPU=$(FREQ) -I$(SRCDIR)
CFLAGS := -mmcu=$(MCU) -Os -std=c99 -Wall -Wextra -Werror CFLAGS := -mmcu=$(MCU) -Os -std=c99 -Wall -Wextra -Werror
OCFLAGS := -j .text -j .data -O ihex OCFLAGS := -j .text -j .data -O ihex
LDFLAGS := -mmcu=$(MCU) LDFLAGS := -mmcu=$(MCU)

BIN
docs/PCA9546.pdf Executable file

Binary file not shown.

12
src/bus/twi.c Normal file
View File

@@ -0,0 +1,12 @@
#include "bus/twi.h"
#include <avr/io.h>
int TWI_Init(void)
{
// Set SCL bit rate to 400kHz
TWSR = 0x00; // TWI status register
TWBR = 0x0C; // TWI bit rate register
return 0;
}

6
src/bus/twi.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef MAD_CORE_BUS_TWI_H
#define MAD_CORE_BUS_TWI_H
int TWI_Init(void);
#endif // MAD_CORE_BUS_TWI_H

93
src/bus/usart.c Normal file
View File

@@ -0,0 +1,93 @@
#include "common.h"
#include "bus/usart.h"
#include <avr/io.h>
#include <avr/interrupt.h>
//#include <util/atomic.h>
#define USART_BAUDRATE 9600
#define USART_RXBUF_SIZE 512
#define USART_TXBUF_SIZE 512
#define USART_RXBUF_MASK (USART_RXBUF_SIZE - 1)
#define USART_TXBUF_MASK (USART_TXBUF_SIZE - 1)
#define USART_BAUD_PRESCALE ((((F_CPU / 16) + \
(USART_BAUDRATE / 2)) / (USART_BAUDRATE)) - 1)
static volatile char rxbuf[USART_RXBUF_SIZE]; // RX ring buffer
static volatile char txbuf[USART_TXBUF_SIZE]; // TX ring buffer
static volatile short rxhead, txhead; // Current write position
static volatile short rxtail, txtail; // Current read position
int USART_Init(void)
{
rxhead = 0;
rxtail = 0;
txhead = 0;
txtail = 0;
UCSRB = BIT(RXCIE); // Handle RXC interrupts
UCSRB |= BIT(RXEN) | BIT(TXEN); // Enable RX and TX circuitry
UCSRC = BIT(URSEL) | BIT(UCSZ0) | BIT(UCSZ1); // Using 8-bit chars
UBRRH = (USART_BAUD_PRESCALE >> 8); // Set baud rate upper byte
UBRRL = USART_BAUD_PRESCALE; // Set baud rate lower byte
return 0;
}
char USART_Getc(void)
{
if (rxhead == rxtail) {
return -1;
}
rxtail = (rxtail + 1) & USART_RXBUF_MASK;
return rxbuf[rxtail];
}
void USART_Putc(char ch)
{
short head;
head = (txhead + 1) & USART_TXBUF_MASK;
while (head == txtail); // Wait for space
txbuf[head] = ch;
txhead = head;
// Enable interrupt
UCSRB |= BIT(UDRIE);
}
// INT: Rx complete
ISR(USART_RXC_vect)
{
short head;
// Next byte ready to read from serial
head = (rxhead + 1) & USART_RXBUF_MASK;
// Free space in RX buffer?
if (head != rxtail) {
// Copy from register
rxbuf[head] = UDR;
rxhead = head;
} else {
// XXX: Discard overflow
}
}
// INT: Data register empty
ISR(USART_UDRE_vect)
{
short tail;
// Anything in TX buffer?
if (txhead != txtail) {
// Write next byte to data register
tail = (txtail + 1) & USART_TXBUF_MASK;
UDR = txbuf[tail];
txtail = tail;
} else {
// Disable interrupt
UCSRB &= ~BIT(UDRIE);
}
}

8
src/bus/usart.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef MAD_CORE_BUS_USART_H
#define MAD_CORE_BUS_USART_H
int USART_Init(void);
void USART_Putc(char ch);
char USART_Getc(void);
#endif // MAD_CORE_BUS_USART_H

View File

@@ -1,29 +0,0 @@
#include "common.h"
#include "serial.h"
#define PREFIX_ERROR(fmt) "Error:" fmt
static bool state;
void Info(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
USART_Print(state, "", fmt, args);
va_end(args);
}
void Error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
USART_Print(state, "Error: ", fmt, args);
va_end(args);
}
void SetStateFlag(bool is_running)
{
state = is_running;
}

View File

@@ -1,23 +1,12 @@
#ifndef MAD_COMMON_H #ifndef MAD_CORE_COMMON_H
#define MAD_COMMON_H #define MAD_CORE_COMMON_H
#include <stdarg.h>
#define BIT(n) (0x1U << (n)) #define BIT(n) (0x1U << (n))
#define UNUSED(s) (void)(s) #define UNUSED(s) (void)(s)
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <util/delay.h>
#define Sleep(ms) _delay_ms(ms)
void Info(const char *fmt, ...); void Info(const char *fmt, ...);
void Error(const char *fmt, ...); void Error(const char *fmt, ...);
void SetStateFlag(bool is_running);
void Enable(void); #endif // MAD_CORE_COMMON_H
void Disable(void);
void SetTemp(long val);
void SetDewp(long val);
#endif // MAD_COMMON_H

24
src/common/common.c Normal file
View File

@@ -0,0 +1,24 @@
#include "common.h"
#include "bus/usart.h"
#include <stdio.h>
static void Puts(const char *str)
{
while (*str) {
USART_Putc(*str++);
}
}
void Info(const char *fmt, ...)
{
va_list ap;
char msg[256];
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
Puts("[CORE] ");
Puts(msg);
Puts("\r\n");
}

View File

@@ -1,78 +1,28 @@
#include "common.h" #include "common.h"
#include "serial.h" #include "bus/usart.h"
#include "parser.h" #include "bus/twi.h"
bool running; #include <avr/interrupt.h>
#include <util/delay.h>
#define Sleep(ms) _delay_ms(ms)
int main(void) int main(int argc, char **argv)
{ {
char ch;
unsigned long i = 1;
USART_Init(); USART_Init();
Enable(); TWI_Init();
for (;;) { sei();
// Process rx ring buffer
while ((ch = USART_GetChar())) {
CMD_Parse(ch);
}
Sleep(3000); Info("Initializing...");
if (running) { Sleep(500);
// TODO: Main program
if (i >= 99999) i = 1; // ADS1115 (ADC)
Info("Fetching sensors #%05lu...", i++); // PCA9546 (Multiplexer TWI)
} // ATH20 (3x)
} // BMP280 (3x)
UNUSED(argc);
UNUSED(argv);
return 0; return 0;
} }
void Enable(void)
{
Info("Parsed 'CMD_RUN' token.");
if (running == true) {
Error("Already running.");
return;
}
running = true;
SetStateFlag(running);
Info("Program started.");
}
void Disable(void)
{
Info("Parsed 'CMD_STOP' token.");
if (running == false) {
Error("Already stopped.");
return;
}
running = false;
SetStateFlag(running);
Info("Program stopped.");
}
void SetTemp(long val)
{
Info("Parsed 'CMD_SET_TEMP', VAL='%ld'.", val);
if (val < 10) {
Error("Given temperature is too low.");
return;
} else if (val > 40) {
Error("Given temperature is too high.");
return;
}
}
void SetDewp(long val)
{
Info("Parsed 'CMD_SET_DEWP', VAL='%ld'.", val);
if (val < 10) {
Error("Given dew point is too low.");
return;
} else if (val > 80) {
Error("Given dew point is too high.");
return;
}
}

View File

@@ -1,217 +0,0 @@
#include "common.h"
#include "serial.h"
#include "parser.h"
/*
* # Supported Commands
*
* - RUN
* - STOP
* - TEMP <num>
* - DEWP <num>
*/
static int state;
static char numbuf[8];
static size_t numlen;
#define PARSE_IDLE 0
#define PARSE_CMD_RUN 1
#define PARSE_CMD_STOP 2
#define PARSE_CMD_SET_TEMP 3
#define PARSE_CMD_SET_DEWP 4
void CMD_Parse(char ch)
{
long num;
#if 0
switch (state) {
case PARSE_IDLE:
if (ch == 'R')
state = PARSE_CMD_RUN;
else if (ch == 'S')
state = PARSE_CMD_STOP;
else if (ch == 'T')
state = PARSE_CMD_SET_TEMP;
else if (ch == 'D')
state = PARSE_CMD_SET_DEWP;
}
break;
case PARSE_CMD_RUN:
break;
case PARSE_CMD_STOP:
break;
case PARSE_CMD_SET_TEMP:
break;
case PARSE_CMD_SET_DEWP:
break;
}
#endif
// Placeholder
switch (state) {
case 0:
if (ch == 'T') // TEMP
state = 1;
if (ch == 'D') // DEWP
state = 6;
if (ch == 'R') // RUN
state = 11;
if (ch == 'S') // STOP
state = 14;
break;
// Parse 'TEMP'
case 1:
if (ch == 'E')
state = 2;
else
state = 0;
break;
case 2:
if (ch == 'M')
state = 3;
else
state = 0;
break;
case 3:
if (ch == 'P')
state = 4;
else
state = 0;
break;
case 4:
if (ch == ' ') {
numlen = 0;
state = 5;
} else {
state = 0;
}
break;
case 5:
// Error: Number too long
if (numlen >= sizeof(numbuf) - 1) {
state = 0;
}
// Parse numeric string
else if ((ch >= '0' && ch <= '9') || (!numlen && ch == '-')) {
numbuf[numlen++] = ch;
}
// Parse terminator and strip decimal
else if (ch == '\n' || ch == '.' || ch == ',') {
numbuf[numlen] = '\0';
num = strtol(numbuf, NULL, 10);
SetTemp(num);
state = 0;
}
// Unexpected character
else {
Error("Unexpected character '%c'.", ch);
state = 0;
}
break;
// Parse 'DEWP'
case 6:
if (ch == 'E')
state = 7;
else
state = 0;
break;
case 7:
if (ch == 'W')
state = 8;
else
state = 0;
break;
case 8:
if (ch == 'P')
state = 9;
else
state = 0;
break;
case 9:
if (ch == ' ') {
numlen = 0;
state = 10;
} else {
state = 0;
}
break;
case 10:
// Error: Number too long
if (numlen >= sizeof(numbuf) - 1) {
state = 0;
}
// Parse numeric string
else if ((ch >= '0' && ch <= '9') || (!numlen && ch == '-')) {
numbuf[numlen++] = ch;
}
// Parse terminator and strip decimal
else if (ch == '\n' || ch == '.' || ch == ',') {
numbuf[numlen] = '\0';
num = strtol(numbuf, NULL, 10);
SetDewp(num);
state = 0;
}
// Unexpected character
else {
Error("Unexpected character '%c'.", ch);
state = 0;
}
break;
// Parse 'RUN'
case 11:
if (ch == 'U')
state = 12;
else
state = 0;
break;
case 12:
if (ch == 'N')
state = 13;
else
state = 0;
break;
case 13:
if (ch == '\n') {
Enable();
}
state = 0;
break;
// Parse 'STOP'
case 14:
if (ch == 'T')
state = 15;
else
state = 0;
break;
case 15:
if (ch == 'O')
state = 16;
else
state = 0;
break;
case 16:
if (ch == 'P')
state = 17;
else
state = 0;
break;
case 17:
if (ch == '\n') {
Disable();
}
state = 0;
break;
}
}

View File

@@ -1,6 +0,0 @@
#ifndef MAD_PARSER_H
#define MAD_PARSER_H
void CMD_Parse(char ch);
#endif // MAD_PARSER_H

View File

@@ -1,89 +0,0 @@
#include "common.h"
#include "serial.h"
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#define USART_BUFSIZE 512
#define USART_BAUDRATE 9600
#define USART_RXBUF_MASK (USART_BUFSIZE - 1)
#define USART_BAUD_PRESCALE ((((F_CPU / 16) + \
(USART_BAUDRATE / 2)) / (USART_BAUDRATE)) - 1)
// TODO: TX ring buffer
static unsigned char rxbuf[USART_BUFSIZE];
static unsigned char rxhead, rxtail;
void USART_Init(void)
{
rxhead = 0;
rxtail = 0;
// Handle RXC interrupt
UCSRB |= (1 << RXCIE);
sei();
UCSRB |= BIT(RXEN) | BIT(TXEN); // Enable rx and tx circuitry
UCSRC |= BIT(URSEL) | BIT(UCSZ0) | BIT(UCSZ1); // 8-bit chars
UBRRH = (USART_BAUD_PRESCALE >> 8); // Baud Rate upper byte
UBRRL = USART_BAUD_PRESCALE; // Baud rate lower byte
}
bool USART_IsDataAvailable(void)
{
return rxhead != rxtail;
}
char USART_GetChar(void)
{
unsigned char newtail;
if (!USART_IsDataAvailable()) {
return '\0'; // Non-blocking
}
newtail = (rxtail + 1) & USART_RXBUF_MASK;
rxtail = newtail;
return rxbuf[newtail];
}
void USART_PutChar(const char ch)
{
// Wait until UDR can receive data
while ((UCSRA & BIT(UDRE)) == 0);
UDR = ch;
}
void USART_Print(bool state, const char *pre, const char *fmt, va_list ap)
{
int len;
char buf[256], *ptr;
UNUSED(state);
len = sprintf(buf, "[CORE] %s", pre); //, state ? 'E' : 'D', pre);
vsnprintf(buf + len, sizeof(buf) - len, fmt, ap);
ptr = buf;
while (*ptr != '\0') {
USART_PutChar(*ptr++);
}
USART_PutChar('\r');
USART_PutChar('\n');
}
// Interrupt Handler
ISR(USART_RXC_vect)
{
unsigned char newhead;
newhead = (rxhead + 1) & USART_RXBUF_MASK;
if (newhead == rxtail) {
// TODO: Handle overflow
} else {
rxhead = newhead;
rxbuf[newhead] = UDR;
}
}

View File

@@ -1,13 +0,0 @@
#ifndef MAD_SERIAL_H
#define MAD_SERIAL_H
#include <stdarg.h>
#include <stdbool.h>
void USART_Init(void);
char USART_GetChar(void);
void USART_PutChar(const char ch);
bool USART_IsDataAvailable(void);
void USART_Print(bool state, const char *pre, const char *fmt, va_list ap);
#endif // MAD_SERIAL_H

View File

View File