Interrupt driven stdin UART driver

This commit is contained in:
Johan Kanflo 2015-10-26 14:30:45 +01:00
parent 8f6c297849
commit 3b194e8894
8 changed files with 161 additions and 71 deletions

View file

@ -52,7 +52,7 @@ long _write_r(struct _reent *r, int fd, const char *ptr, int len )
} }
/* syscall implementation for stdio read from UART */ /* syscall implementation for stdio read from UART */
long _read_r( struct _reent *r, int fd, char *ptr, int len ) __attribute__ ((weak)) long _read_r( struct _reent *r, int fd, char *ptr, int len )
{ {
int ch, i; int ch, i;

View file

@ -1,3 +1,3 @@
PROGRAM=terminal PROGRAM=terminal
EXTRA_COMPONENTS=extras/serial-driver EXTRA_COMPONENTS=extras/stdin_uart_interrupt
include ../../common.mk include ../../common.mk

View file

@ -1,6 +1,6 @@
/* Serial terminal example /* Serial terminal example
* UART RX is interrupt driven * UART RX is interrupt driven
* Read characters until \n and echo back * Implements a simple GPIO terminal for setting and clearing GPIOs
* *
* This sample code is in the public domain. * This sample code is in the public domain.
*/ */
@ -8,30 +8,110 @@
#include <stdint.h> #include <stdint.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <espressif/esp_common.h> #include <string.h>
#include <serial_driver.h> #include <esp8266.h>
#include <esp/uart.h>
#include "FreeRTOS.h"
#include "task.h"
#define MAX_ARGC (10)
#define BUFFER_SIZE (81) #define BUFFER_SIZE (81)
void user_init(void) static void cmd_on(uint32_t argc, char *argv[])
{ {
char buffer[BUFFER_SIZE]; if (argc >= 2) {
uint32_t i = 0; for(int i=1; i<argc; i++) {
sdk_uart_div_modify(0, UART_CLK_FREQ / 115200); uint8_t gpio_num = atoi(argv[i]);
gpio_enable(gpio_num, GPIO_OUTPUT);
gpio_write(gpio_num, true);
printf("On %d\n", gpio_num);
}
} else {
printf("Error: missing gpio number.\n");
}
}
static void cmd_off(uint32_t argc, char *argv[])
{
if (argc >= 2) {
for(int i=1; i<argc; i++) {
uint8_t gpio_num = atoi(argv[i]);
gpio_enable(gpio_num, GPIO_OUTPUT);
gpio_write(gpio_num, false);
printf("Off %d\n", gpio_num);
}
} else {
printf("Error: missing gpio number.\n");
}
}
static void cmd_help(uint32_t argc, char *argv[])
{
printf("on <gpio number> [ <gpio number>]+ Set gpio to 1\n");
printf("off <gpio number> [ <gpio number>]+ Set gpio to 0\n");
printf("sleep Take a nap\n");
printf("\nExample:\n");
printf(" on 0<enter> switches on gpio 0\n");
printf(" on 0 2 4<enter> switches on gpios 0, 2 and 4\n");
}
static void cmd_sleep(uint32_t argc, char *argv[])
{
printf("Type away while I take a 2 second nap (ie. let you test the UART HW FIFO\n");
vTaskDelay(2000 / portTICK_RATE_MS);
}
static void handle_command(char *cmd)
{
char *argv[MAX_ARGC];
int argc = 1;
char *temp, *rover;
memset((void*) argv, 0, sizeof(argv));
argv[0] = cmd;
rover = cmd;
// Split string "<command> <argument 1> <argument 2> ... <argument N>"
// into argv, argc style
while(argc < MAX_ARGC && (temp = strstr(rover, " "))) {
rover = &(temp[1]);
argv[argc++] = rover;
*temp = 0;
}
if (strlen(argv[0]) > 0) {
if (strcmp(argv[0], "help") == 0) cmd_help(argc, argv);
else if (strcmp(argv[0], "on") == 0) cmd_on(argc, argv);
else if (strcmp(argv[0], "off") == 0) cmd_off(argc, argv);
else if (strcmp(argv[0], "sleep") == 0) cmd_sleep(argc, argv);
else printf("Unknown command %s, try 'help'\n", argv[0]);
}
}
static void gpiomon()
{
char ch;
char cmd[81];
int i = 0;
printf("\n\n\nWelcome to gpiomon. Type 'help<enter>' for, well, help\n");
printf("%% ");
while(1) { while(1) {
char ch;
// The thread will block here until there is data available
// NB. read(...) may be called from user_init or from a thread
// We can check how many characters are available in the RX buffer
// with uint32_t uart0_num_char(void);
if (read(0, (void*)&ch, 1)) { // 0 is stdin if (read(0, (void*)&ch, 1)) { // 0 is stdin
if (i == BUFFER_SIZE-2 || ch == '\n') { printf("%c", ch);
buffer[i] = 0; if (ch == '\n' || ch == '\r') {
printf("Unknown command: %s\n", (char*) buffer); cmd[i] = 0;
i = 0; i = 0;
printf("\n");
handle_command((char*) cmd);
printf("%% ");
} else { } else {
buffer[i++] = ch; if (i < sizeof(cmd)) cmd[i++] = ch;
} }
} }
} }
} }
void user_init(void)
{
uart_set_baud(0, 115200);
setbuf(stdout, NULL);
gpiomon();
}

View file

@ -1,11 +0,0 @@
# Component makefile for extras/serial-driver
#
# See examples/terminal for usage
INC_DIRS += $(ROOT)extras/serial-driver
# args for passing into compile rule generation
extras/serial-driver_INC_DIR = $(ROOT)extras/serial-driver
extras/serial-driver_SRC_DIR = $(ROOT)extras/serial-driver
$(eval $(call component_compile_rules,extras/serial-driver))

View file

@ -0,0 +1,11 @@
This module adds interrupt driven receive on UART 0. Using semaphores, a thread
calling read(...) when no data is available will block in an RTOS expected
manner until data arrives.
This allows for a background thread running a serial terminal in your program
for debugging and state inspection consuming no CPU cycles at all. Not using
this module will make that thread while(1) until data arrives.
No code changes are needed for adding this module, all you need to do is to add
it to EXTRA_COMPONENTS and add the directive configUSE_COUNTING_SEMAPHORES from
FreeRTOSConfig.h in examples/terminal to your project.

View file

@ -0,0 +1,13 @@
# Component makefile for extras/stdin_uart_interrupt
#
# See examples/terminal for usage. Well, actually there is no need to see it
# for 'usage' as this module is a drop-in replacement for the original polled
# version of reading from the UART.
INC_DIRS += $(ROOT)extras/stdin_uart_interrupt
# args for passing into compile rule generation
extras/stdin_uart_interrupt_INC_DIR = $(ROOT)extras/stdin_uart_interrupt
extras/stdin_uart_interrupt_SRC_DIR = $(ROOT)extras/stdin_uart_interrupt
$(eval $(call component_compile_rules,extras/stdin_uart_interrupt))

View file

@ -23,8 +23,6 @@
*/ */
#include <esp8266.h> #include <esp8266.h>
#include <espressif/esp8266/esp8266.h>
#include <espressif/esp8266/uart_register.h>
#include <FreeRTOS.h> #include <FreeRTOS.h>
#include <semphr.h> #include <semphr.h>
@ -35,36 +33,36 @@
// IRQ driven UART RX driver for ESP8266 written for use with esp-open-rtos // IRQ driven UART RX driver for ESP8266 written for use with esp-open-rtos
// TODO: Handle UART1 // TODO: Handle UART1
#define UART0_RX_SIZE (81)
#ifndef UART0 #ifndef UART0
#define UART0 (0) #define UART0 (0)
#endif #endif
#define UART0_RX_SIZE (128) // ESP8266 UART HW FIFO size
static xSemaphoreHandle uart0_sem = NULL; static xSemaphoreHandle uart0_sem = NULL;
static char rx_buf[UART0_RX_SIZE];
static uint8_t rd_pos = 0;
static uint8_t wr_pos = 0;
static bool inited = false; static bool inited = false;
static void uart0_rx_init(void); static void uart0_rx_init(void);
IRAM void uart0_rx_handler(void) IRAM void uart0_rx_handler(void)
{ {
// TODO: Handle UART1, see reg 0x3ff20020, bit2, bit0 represents uart1 and uart0 respectively // TODO: Handle UART1, see reg 0x3ff20020, bit2, bit0 represents uart1 and uart0 respectively
if (UART_RXFIFO_FULL_INT_ST != (READ_PERI_REG(UART_INT_ST(UART0)) & UART_RXFIFO_FULL_INT_ST)) { if (!UART(UART0).INT_STATUS & UART_INT_STATUS_RXFIFO_FULL) {
return; return;
} }
WRITE_PERI_REG(UART_INT_CLR(UART0), UART_RXFIFO_FULL_INT_CLR); // printf(" [%08x (%d)]\n", READ_PERI_REG(UART_INT_ST(UART0)), READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S));
while (READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S)) { if (UART(UART0).INT_STATUS & UART_INT_STATUS_RXFIFO_FULL) {
char ch = READ_PERI_REG(UART_FIFO(UART0)) & 0xff; UART(UART0).INT_CLEAR = UART_INT_CLEAR_RXFIFO_FULL;
uint8_t wr_next = (wr_pos+1) % UART0_RX_SIZE; if (UART(UART0).STATUS & (UART_STATUS_RXFIFO_COUNT_M << UART_STATUS_RXFIFO_COUNT_S)) {
if (wr_next != rd_pos) { long int xHigherPriorityTaskWoken;
rx_buf[wr_pos] = ch; _xt_isr_mask(1 << INUM_UART);
wr_pos = (wr_pos+1) % UART0_RX_SIZE; _xt_clear_ints(1<<INUM_UART);
xSemaphoreGiveFromISR(uart0_sem, NULL); xSemaphoreGiveFromISR(uart0_sem, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken) {
portYIELD();
}
} }
} else {
printf("Error: unexpected uart irq, INT_STATUS 0x%02x\n", UART(UART0).INT_STATUS);
} }
} }
@ -72,26 +70,23 @@ uint32_t uart0_num_char(void)
{ {
uint32_t count; uint32_t count;
if (!inited) uart0_rx_init(); if (!inited) uart0_rx_init();
_xt_isr_mask(1 << INUM_UART); count = UART(UART0).STATUS & (UART_STATUS_RXFIFO_COUNT_M << UART_STATUS_RXFIFO_COUNT_S);
if (rd_pos > wr_pos) count = rd_pos - wr_pos;
else count = wr_pos - rd_pos;
_xt_isr_unmask(1 << INUM_UART);
return count; return count;
} }
// _read_r in core/newlib_syscalls.c will be skipped in favour of this function // _read_r in core/newlib_syscalls.c will be skipped by the linker in favour
// of this function
long _read_r(struct _reent *r, int fd, char *ptr, int len) long _read_r(struct _reent *r, int fd, char *ptr, int len)
{ {
if (!inited) uart0_rx_init(); if (!inited) uart0_rx_init();
for(int i = 0; i < len; i++) { for(int i = 0; i < len; i++) {
char ch; if (!(UART(UART0).STATUS & (UART_STATUS_RXFIFO_COUNT_M << UART_STATUS_RXFIFO_COUNT_S))) {
if (xSemaphoreTake(uart0_sem, portMAX_DELAY)) {
_xt_isr_mask(1 << INUM_UART);
ch = rx_buf[rd_pos];
rd_pos = (rd_pos+1) % UART0_RX_SIZE;
_xt_isr_unmask(1 << INUM_UART); _xt_isr_unmask(1 << INUM_UART);
ptr[i] = ch; if (!xSemaphoreTake(uart0_sem, portMAX_DELAY)) {
printf("\nFailed to get sem\n");
}
} }
ptr[i] = UART(UART0).FIFO & (UART_FIFO_DATA_M << UART_FIFO_DATA_S);
} }
return len; return len;
} }
@ -104,16 +99,19 @@ static void uart0_rx_init(void)
_xt_isr_attach(INUM_UART, uart0_rx_handler); _xt_isr_attach(INUM_UART, uart0_rx_handler);
_xt_isr_unmask(1 << INUM_UART); _xt_isr_unmask(1 << INUM_UART);
//clear rx and tx fifo,not ready // clear rx and tx fifo,not ready
SET_PERI_REG_MASK(UART_CONF0(UART0), UART_RXFIFO_RST | UART_TXFIFO_RST); uint32_t conf = UART(UART0).CONF0;
CLEAR_PERI_REG_MASK(UART_CONF0(UART0), UART_RXFIFO_RST | UART_TXFIFO_RST); UART(UART0).CONF0 = conf | UART_CONF0_RXFIFO_RESET | UART_CONF0_TXFIFO_RESET;
UART(UART0).CONF0 = conf & ~(UART_CONF0_RXFIFO_RESET | UART_CONF0_TXFIFO_RESET);
//set rx fifo trigger // set rx fifo trigger
WRITE_PERI_REG(UART_CONF1(UART0), (trig_lvl & UART_RXFIFO_FULL_THRHD) << UART_RXFIFO_FULL_THRHD_S); UART(UART0).CONF1 |= (trig_lvl & UART_CONF1_RXFIFO_FULL_THRESHOLD_M) << UART_CONF1_RXFIFO_FULL_THRESHOLD_S;
// clear all interrupts
UART(UART0).INT_CLEAR = 0x1ff;
// enable rx_interrupt
UART(UART0).INT_ENABLE = UART_INT_ENABLE_RXFIFO_FULL;
//clear all interrupt
WRITE_PERI_REG(UART_INT_CLR(UART0), 0xffff);
//enable rx_interrupt
SET_PERI_REG_MASK(UART_INT_ENA(UART0), UART_RXFIFO_FULL_INT_ENA);
inited = true; inited = true;
} }

View file

@ -22,12 +22,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef __I2C_H__ #ifndef __STDIN_UART_INTERRUPT_H__
#define __I2C_H__ #define __STDIN_UART_INTERRUPT_H__
#endif
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
// Return number of characters waiting in UART0 // Return number of characters waiting in UART0
uint32_t uart0_num_char(void); uint32_t uart0_num_char(void);
#endif