extras/stdin_uart_interrupt: stdin via IRQ driven RX driver on UART0

See examples/terminal/ for usage
This commit is contained in:
Johan Kanflo 2015-08-07 22:41:13 +02:00 committed by Angus Gratton
parent e9d6e81b76
commit ffa1550193
8 changed files with 306 additions and 1 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 */
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;

View file

@ -0,0 +1,12 @@
/* Terminal FreeRTOSConfig overrides.
This is intended as an example of overriding some of the default FreeRTOSConfig settings,
which are otherwise found in FreeRTOS/Source/include/FreeRTOSConfig.h
*/
/* The serial driver depends on counting semaphores */
#define configUSE_COUNTING_SEMAPHORES 1
/* Use the defaults for everything else */
#include_next<FreeRTOSConfig.h>

View file

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

View file

@ -0,0 +1,117 @@
/* Serial terminal example
* UART RX is interrupt driven
* Implements a simple GPIO terminal for setting and clearing GPIOs
*
* This sample code is in the public domain.
*/
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <esp8266.h>
#include <esp/uart.h>
#include "FreeRTOS.h"
#include "task.h"
#define MAX_ARGC (10)
#define BUFFER_SIZE (81)
static void cmd_on(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, 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) {
if (read(0, (void*)&ch, 1)) { // 0 is stdin
printf("%c", ch);
if (ch == '\n' || ch == '\r') {
cmd[i] = 0;
i = 0;
printf("\n");
handle_command((char*) cmd);
printf("%% ");
} else {
if (i < sizeof(cmd)) cmd[i++] = ch;
}
}
}
}
void user_init(void)
{
uart_set_baud(0, 115200);
setbuf(stdout, NULL);
gpiomon();
}

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

@ -0,0 +1,117 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Johan Kanflo (github.com/kanflo)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <esp8266.h>
#include <FreeRTOS.h>
#include <semphr.h>
#if (configUSE_COUNTING_SEMAPHORES == 0)
#error "You need to define configUSE_COUNTING_SEMAPHORES in a local FreeRTOSConfig.h, see examples/terminal/FreeRTOSConfig.h"
#endif
// IRQ driven UART RX driver for ESP8266 written for use with esp-open-rtos
// TODO: Handle UART1
#ifndef UART0
#define UART0 (0)
#endif
#define UART0_RX_SIZE (128) // ESP8266 UART HW FIFO size
static xSemaphoreHandle uart0_sem = NULL;
static bool inited = false;
static void uart0_rx_init(void);
IRAM void uart0_rx_handler(void)
{
// TODO: Handle UART1, see reg 0x3ff20020, bit2, bit0 represents uart1 and uart0 respectively
if (!UART(UART0).INT_STATUS & UART_INT_STATUS_RXFIFO_FULL) {
return;
}
// printf(" [%08x (%d)]\n", READ_PERI_REG(UART_INT_ST(UART0)), READ_PERI_REG(UART_STATUS(UART0)) & (UART_RXFIFO_CNT << UART_RXFIFO_CNT_S));
if (UART(UART0).INT_STATUS & UART_INT_STATUS_RXFIFO_FULL) {
UART(UART0).INT_CLEAR = UART_INT_CLEAR_RXFIFO_FULL;
if (UART(UART0).STATUS & (UART_STATUS_RXFIFO_COUNT_M << UART_STATUS_RXFIFO_COUNT_S)) {
long int xHigherPriorityTaskWoken;
_xt_isr_mask(1 << INUM_UART);
_xt_clear_ints(1<<INUM_UART);
xSemaphoreGiveFromISR(uart0_sem, &xHigherPriorityTaskWoken);
if(xHigherPriorityTaskWoken) {
portYIELD();
}
}
} else {
printf("Error: unexpected uart irq, INT_STATUS 0x%02x\n", UART(UART0).INT_STATUS);
}
}
uint32_t uart0_num_char(void)
{
uint32_t count;
if (!inited) uart0_rx_init();
count = UART(UART0).STATUS & (UART_STATUS_RXFIFO_COUNT_M << UART_STATUS_RXFIFO_COUNT_S);
return count;
}
// _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)
{
if (!inited) uart0_rx_init();
for(int i = 0; i < len; i++) {
if (!(UART(UART0).STATUS & (UART_STATUS_RXFIFO_COUNT_M << UART_STATUS_RXFIFO_COUNT_S))) {
_xt_isr_unmask(1 << INUM_UART);
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;
}
static void uart0_rx_init(void)
{
int trig_lvl = 1;
uart0_sem = xSemaphoreCreateCounting(UART0_RX_SIZE, 0);
_xt_isr_attach(INUM_UART, uart0_rx_handler);
_xt_isr_unmask(1 << INUM_UART);
// clear rx and tx fifo,not ready
uint32_t conf = UART(UART0).CONF0;
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
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;
inited = true;
}

View file

@ -0,0 +1,32 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Johan Kanflo (github.com/kanflo)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef __STDIN_UART_INTERRUPT_H__
#define __STDIN_UART_INTERRUPT_H__
#include <stdint.h>
// Return number of characters waiting in UART0
uint32_t uart0_num_char(void);
#endif