Browse Source

cherry-pick from busvoodoo branch, part 2

spark_strober
King Kévin 4 years ago
parent
commit
e14f6bccd6
  1. 2
      .gitignore
  2. 1
      Rakefile
  3. 8
      application.c
  4. 436
      lib/terminal.c
  5. 34
      lib/terminal.h
  6. 12
      lib/uart.c
  7. 14
      lib/uart.h
  8. 148
      lib/usart.c
  9. 47
      lib/usart.h

2
.gitignore vendored

@ -7,4 +7,6 @@ @@ -7,4 +7,6 @@
*.elf
*.bin
*.swp
*.swo
.gdb_history
doc/

1
Rakefile

@ -88,6 +88,7 @@ archflags = "-mthumb -mcpu=cortex-m3 -msoft-float" @@ -88,6 +88,7 @@ archflags = "-mthumb -mcpu=cortex-m3 -msoft-float"
desc "compile firmwares"
task :default => FIRMWARES
task :compile => FIRMWARES
FIRMWARES.each do |firmware|
desc "compile #{firmware} firmware"

8
application.c

@ -98,7 +98,10 @@ static void process_command(char* str) @@ -98,7 +98,10 @@ static void process_command(char* str)
// parse command
if (0==strcmp(word,"h") || 0==strcmp(word,"help") || 0==strcmp(word,"?")) {
printf("available commands:\n");
printf("led [on|off|toggle]\n");
printf("l|led [on|off|toggle]\n");
printf("s|self-test\n");
printf("p|pin-test\n");
printf("r|reset\n");
} else if (0==strcmp(word,"l") || 0==strcmp(word,"led")) {
word = strtok(NULL,delimiter);
if (!word) {
@ -156,6 +159,9 @@ static void process_command(char* str) @@ -156,6 +159,9 @@ static void process_command(char* str)
rtc_set_counter_val(time_rtc); // save time to internal RTC
printf("date set\n");
}
} else if (0==strcmp(word,"r") || 0==strcmp(word,"reset")) {
scb_reset_system(); // reset device
while (true); // wait for the reset to happen
} else {
goto error;
}

436
lib/terminal.c

@ -0,0 +1,436 @@ @@ -0,0 +1,436 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
/** terminal prompt interface (code)
* @note allows line editing and supports some ANSI escape codes
* @file terminal.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2018
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdbool.h> // boolean type
#include <stdlib.h> // standard utilities
#include <string.h> // string utilities
/* own libraries */
#include "global.h" // global definitions
#include "terminal.h" // own definitions
#include "print.h" // printing utilities
char* terminal_prefix = NULL;
void (*terminal_process)(char* line) = NULL;
/** buffer to store user input and keep history */
static char terminal_buffer[16] = {0};
/** how much of the buffer is user */
static uint16_t terminal_end = 0;
/** current position in the buffer */
static uint16_t terminal_pos = 0;
/** start position or current line in the buffer */
static uint16_t terminal_line = 0;
/** is the current line the last one */
static bool terminal_last = true;
/** currently inserting or replacing characters */
static bool terminal_insert = true;
/** current escape code */
static char escape_code[8] = {0};
/** current position in the escape code */
static uint8_t escape_pos = 0;
/** remove one line from buffer start and shift rest to start
* @return if one line has been removed
*/
static bool terminal_remove_line(void)
{
if (0==terminal_line) { // be sure we are currently not on the first line
return false;
}
uint16_t line_end = strlen(&terminal_buffer[0]); // get end of line
if (terminal_end<=line_end) { // be sure there is a line to delete
return false;
}
for (uint16_t i=line_end+1; i<=terminal_end && i<LENGTH(terminal_buffer); i++) { // line buffer after the line to start
terminal_buffer[i-line_end-1] = terminal_buffer[i];
}
if (terminal_end>line_end+1) { // update buffer end
terminal_end -= line_end+1;
} else {
terminal_end = 0;
}
if (terminal_pos>line_end+1) { // update buffer position
terminal_pos -= line_end+1;
} else {
terminal_pos = 0;
}
if (terminal_line>line_end+1) { // update line position
terminal_line -= line_end+1;
} else {
terminal_line = 0;
}
if (0==terminal_line) { // update if we are on the last line
terminal_last = true;
}
return true;
}
/** shift with rotate current characters to other position
* @note this uses a recursive function
* @param[in] to_copy position of character(s) to shift
* @param[in] nb_copy number of characters to shift
* @param[in] to_shift where to shift the characters to
*/
static void terminal_shift_line(uint16_t to_copy, uint16_t nb_copy, uint16_t to_shift)
{
char c = terminal_buffer[to_copy];
terminal_buffer[to_copy] = terminal_buffer[to_shift];
if (to_shift<terminal_end) { // characters to shift remain
terminal_shift_line(to_copy+1, nb_copy>0 ? nb_copy-1 : 0, to_shift+1); // shift next character
}
if (nb_copy>0) {
terminal_buffer[terminal_end-nb_copy] = c; // place back
}
}
/** copy current line to last line */
static void terminal_copy_line(void)
{
if (terminal_last) { // current line is already last line
return; // do nothing
}
uint16_t line_len = strlen(&terminal_buffer[terminal_line]);
while (terminal_end>=LENGTH(terminal_buffer)-line_len-1 && terminal_remove_line()); // delete line if not enough space
if (terminal_end<LENGTH(terminal_buffer)-line_len-1) { // there is enough space to copy the line
for (uint16_t i=0; i<line_len; i++) { // copy line to end of buffer
terminal_buffer[terminal_end+i+1] = terminal_buffer[terminal_line+i]; // copy character
}
terminal_pos += (terminal_end+1-terminal_line); // update current position
terminal_line = terminal_end+1; // update line position
terminal_end += line_len+1; // update buffer end position
terminal_buffer[terminal_end] = '\0'; // ensure end is terminated
} else if (0==terminal_line) { // shift (first) line to end of buffer
terminal_shift_line(0, line_len, line_len+1); // shift line
terminal_line = terminal_end-line_len; // update line position
terminal_pos += terminal_line; // update current position
// terminal_end did not change
}
terminal_last = true; // now we are on the last line
}
/** process current escape code */
static void terminal_process_escape(void)
{
if (escape_pos<2) { // the escape code must have at least 2 bytes (C1 and final)
return;
}
switch (escape_code[0]) { // process escape code according to C1
case '[': // CSI - Control Sequence Introducer
switch (escape_code[escape_pos-1]) { // process CSI code
case 'A': // CUU - cursor up
{
uint16_t n = 1; // number of cells to move
if (escape_pos>2) { // number of cells provided
escape_code[escape_pos-1] = '\0'; // terminate string
n = atoi(&escape_code[1]); // get number of cells
}
while (n--) { // go up number of line
if (0==terminal_line) { // stop if we are already at the top line
break;
}
uint16_t terminal_line_new=0; // new line start
for (uint16_t pos=0; pos<terminal_line-1 && pos<LENGTH(terminal_buffer); pos++) { // find for the last line before the current
if ('\0'==terminal_buffer[pos]) { // new line found
terminal_line_new = pos+1; // save new line
}
}
if (terminal_pos==terminal_line+strlen(&terminal_buffer[terminal_line])) { // if the position is the end of the current line
terminal_pos=terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set position to end of new line
} else {
terminal_pos -= (terminal_line-terminal_line_new); // move position to new line
if (terminal_pos>terminal_line_new+strlen(&terminal_buffer[terminal_line_new])) { // position is outside of line
terminal_pos = terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set new position to end of line
}
}
terminal_line = terminal_line_new; // set new line start
terminal_last = (terminal_end==terminal_line+strlen(&terminal_buffer[terminal_line])); // check if we are on the last line
}
}
break;
case 'B': // CUD - cursor down
{
uint16_t n = 1; // number of cells to move
if (escape_pos>2) { // number of cells provided
escape_code[escape_pos-1] = '\0'; // terminate string
n = atoi(&escape_code[1]); // get number of cells
}
while (n--) { // go down number of line
if (terminal_last) { // stop if we are already at the last line
break;
}
uint16_t terminal_line_new = terminal_line+strlen(&terminal_buffer[terminal_line])+1; // line start for the next line
if (terminal_pos==terminal_line+strlen(&terminal_buffer[terminal_line])) { // if the position is the end of the current line
terminal_pos=terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set position to end of new line
} else {
terminal_pos += (terminal_line_new-terminal_line); // move position to new line
if (terminal_pos>terminal_line_new+strlen(&terminal_buffer[terminal_line_new])) { // position is outside of line
terminal_pos = terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set new position to end of line
}
}
terminal_line = terminal_line_new; // set new line start
terminal_last = (terminal_end==terminal_line+strlen(&terminal_buffer[terminal_line])); // check if we are on the last line
}
}
break;
case 'C': // CUF - cursor forward
{
uint16_t n = 1; // number of cells to move
if (escape_pos>2) { // number of cells provided
escape_code[escape_pos-1] = '\0'; // terminate string
n = atoi(&escape_code[1]); // get number of cells
}
while (n--) { // go right number of moves
if (terminal_pos>=terminal_line+strlen(&terminal_buffer[terminal_line])) { // stop if we are already at the end
break;
}
terminal_pos++;
}
}
break;
case 'D': // CUB - cursor back
{
uint16_t n = 1; // number of cells to move
if (escape_pos>2) { // number of cells provided
escape_code[escape_pos-1] = '\0'; // terminate string
n = atoi(&escape_code[1]); // get number of cells
}
while (n--) { // go left number of moves
if (terminal_pos<=terminal_line) { // stop if we are already at the beginning
break;
}
terminal_pos--;
}
}
break;
case '~': // special key
if (3==escape_pos) { // we only expect one parameter bytes
switch (escape_code[1]) {
case '2': // insert
terminal_insert = !terminal_insert; // toggle insert/replace mode
break;
case '3': // delete
if (!terminal_last) { // we are not editing the last line
terminal_copy_line(); // make current line the last line
}
if (terminal_pos<terminal_end) { // be sure we are not at the end of the line, where there is nothing to erase
for (uint16_t i=terminal_pos; i<terminal_end && i<LENGTH(terminal_buffer)-1; i++) { // delete character by shifting end of line
terminal_buffer[i] = terminal_buffer[i+1]; // shift character
}
terminal_end--; // shorten line
}
break;
case '7': // home
terminal_pos = terminal_line; // set position to beginning of line
break;
case '8': // end
terminal_pos = terminal_line+strlen(&terminal_buffer[terminal_line]); // set position to end of line
break;
case '5': // page up
if (terminal_line>0) {
uint16_t terminal_line_new = 0; // set to first line
if (terminal_pos==terminal_line+strlen(&terminal_buffer[terminal_line])) { // if the position is the end of the current line
terminal_pos=terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set position to end of new line
} else {
terminal_pos -= (terminal_line-terminal_line_new); // move position to new line
if (terminal_pos>terminal_line_new+strlen(&terminal_buffer[terminal_line_new])) { // position is outside of line
terminal_pos = terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set new position to end of line
}
}
terminal_line = terminal_line_new; // set new line start
terminal_last = (terminal_end==terminal_line+strlen(&terminal_buffer[terminal_line])); // check if we are on the last line
}
break;
case '6': // page down
if (!terminal_last) { // stop if we are already at the last line
uint16_t terminal_line_new = terminal_line; // start last line search
for (uint16_t i=terminal_line_new; i<terminal_end-1; i++) { // search for last line
if ('\0'==terminal_buffer[i]) { // end of line found
terminal_line_new = i+1; // set new line start
}
}
if (terminal_pos==terminal_line+strlen(&terminal_buffer[terminal_line])) { // if the position is the end of the current line
terminal_pos=terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set position to end of new line
} else {
terminal_pos += (terminal_line_new-terminal_line); // move position to new line
if (terminal_pos>terminal_line_new+strlen(&terminal_buffer[terminal_line_new])) { // position is outside of line
terminal_pos = terminal_line_new+strlen(&terminal_buffer[terminal_line_new]); // set new position to end of line
}
}
terminal_line = terminal_line_new; // set new line start
terminal_last = (terminal_end==terminal_line+strlen(&terminal_buffer[terminal_line])); // check if we are on the last line
}
break;
}
}
break;
default: // we don't handle other codes
break; // do nothing
}
break;
default: // we don't handle this code
break; // do nothing
}
}
/** print current line and set position */
static void terminal_print_line(void)
{
printf("\r\x1b[K%s%s", terminal_prefix ? terminal_prefix : "", &terminal_buffer[terminal_line]); // erase line and display last one
if (terminal_pos<terminal_line+strlen(&terminal_buffer[terminal_line])) { // cursor is not at the end of the line
printf("\x1b[%uD", terminal_line+strlen(&terminal_buffer[terminal_line])-terminal_pos); // set position by going back
}
}
void terminal_setup(void) {
// reset all buffers
terminal_buffer[0] = '\0';
terminal_end = 0;
terminal_pos = 0;
terminal_line = 0;
terminal_last = true;
escape_pos = 0;
// start prompt
terminal_send(0);
}
void terminal_send(volatile char c)
{
static char newline_last = 0; // last new linefeed or carrier return character received
if (escape_pos) { // currently receiving an escape code
if (1==escape_pos) { // we received C0 and expected C1
if (c>=0x40 && c<=0x5f) { // data is in the right range
escape_code[0] = c; // save C1
escape_pos++; // got to next position
} else { // this is not a C1 code
escape_pos = 0; // stop saving the escape code
terminal_send(c); // process data as non-escape code
}
} else { // after C1 we expect a parameters, intermediate, or final byte
if (c>=0x20 && c<=0x3f) { // received parameter (0x30-0x3f) or intermediate (0x20-0x2f) byte
if (escape_pos<=LENGTH(escape_code)) {
escape_code[escape_pos-1] = c; // save parameter byte
escape_pos++; // go to next position
}
} else if (c>=0x40 && c<=0x7f) { // received final byte
if (escape_pos<=LENGTH(escape_code)) {
escape_code[escape_pos-1] = c; // save final byte
}
terminal_process_escape(); // process escape code since we received the final byte
escape_pos = 0; // stop saving the escape code
terminal_print_line(); // print current line since if might have changed
} else { // this is not a expected byte
escape_pos = 0; // stop saving the escape code
terminal_send(c); // process data as non-escape code
}
}
} else {
if (0==c) { // string end received
terminal_print_line(); // only update line
} else if (0x1b==c) { // receiving new escape code (ESC)
escape_pos = 1; // start filling buffer
} else if (0x03==c) { // received CRTL+C
if (!terminal_last) { // we are not on the last line
uint16_t terminal_line_new = terminal_line; // start last line search
for (uint16_t i=terminal_line_new; i<terminal_end-1; i++) { // search for last line
if ('\0'==terminal_buffer[i]) { // end of line found
terminal_line_new = i+1; // set new line start
}
}
terminal_line = terminal_line_new; // set new line start
terminal_last = true; // remember we are on the last line
}
if (terminal_line==terminal_end) { // line is empty
// nothing to do
} else {
// do not process current line
while (terminal_end>=LENGTH(terminal_buffer)-1 && terminal_remove_line()); // delete line if not enough space
if (terminal_end<LENGTH(terminal_buffer)-1) { // now there is space
terminal_end++; // shift end to new line
} else { // the current last line takes all the space -> erase it
terminal_end = 0; // update end
}
terminal_buffer[terminal_end] = '\0'; // ensure end is terminated
}
terminal_pos = terminal_end; // set position to new line
terminal_line = terminal_end; // update line position
printf("\n"); // go to new line
terminal_print_line(); // print current empty line
} else { // all other bytes do some line editing
if (!terminal_last) { // we are not editing the last line
terminal_copy_line(); // make current line the last line
}
if ('\r'== c || '\n'==c) { // line finished
if ('\r'==newline_last && '\n'==c) { // windows newline received
// this newline has already been handled before
} else {
printf("\n"); // print new line
if (terminal_process) {
(*terminal_process)(&terminal_buffer[terminal_line]); // process line
}
if (strlen(&terminal_buffer[terminal_line])>0) { // only store non-empty line
while (terminal_end>=LENGTH(terminal_buffer)-1 && terminal_remove_line()); // delete line if not enough space
if (terminal_end<LENGTH(terminal_buffer)-1) { // now there is space
terminal_end++; // shift end to new line
} else { // the current last line takes all the space -> erase it
terminal_end = 0; // update end
}
terminal_buffer[terminal_end] = '\0'; // ensure end is terminated
terminal_pos = terminal_end; // set position to new line
terminal_line = terminal_pos; // update line position
}
}
newline_last = c; // remember last character
} else if (0x7f==c) { // backspace
if (terminal_pos>terminal_line) { // we are not at the beginning of the line
for (uint16_t i=terminal_pos-1; i<terminal_end && i<LENGTH(terminal_buffer)-1; i++) { // delete character by shifting end of line
terminal_buffer[i] = terminal_buffer[i+1]; // shift character
}
terminal_end--; // shorten line
terminal_pos--; // move position back
}
} else if (terminal_insert) {
while (terminal_end>=LENGTH(terminal_buffer)-1 && terminal_remove_line()); // delete line if not enough space
if (terminal_end<LENGTH(terminal_buffer)-1) { // there is space to move the end
for (int16_t i=terminal_end; i>=terminal_pos; i--) { // shift buffer
terminal_buffer[i+1] = terminal_buffer[i];
}
terminal_buffer[terminal_pos++] = c; // insert new character
terminal_buffer[++terminal_end] = '\0'; // update end
}
} else { // replace current character
while (terminal_pos==terminal_end && terminal_end>=LENGTH(terminal_buffer)-1 && terminal_remove_line()); // delete line if not enough space
terminal_buffer[terminal_pos] = c; // replace current character
if (terminal_pos<LENGTH(terminal_buffer)-1) { // go to next character (if possible)
terminal_pos++;
}
if (terminal_pos>terminal_end) { // update end if it moved
terminal_end = terminal_pos; // move end
terminal_buffer[terminal_end] = '\0'; // ensure end is terminated
}
}
terminal_print_line(); // print current line
}
}
}

34
lib/terminal.h

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
/** terminal prompt interface (API)
* @note allows line editing and supports some ANSI escape codes
* @file terminal.h
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2018
*/
/** terminal prompt prefix */
extern char* terminal_prefix;
/** initialize terminal prompt */
void terminal_setup(void);
/** send character to terminal */
void terminal_send(char c);
/** called when a line is entered
* @param[in] line line entered by user (NULL on CTRL+D)
*/
extern void (*terminal_process)(char* line);

12
lib/uart.c

@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
/** library for UART communication (code)
* @file uart.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016
* @date 2016-2017
* @note peripherals used: USART @ref uart
*/
@ -34,19 +34,19 @@ @@ -34,19 +34,19 @@
#include "uart.h" // UART header and definitions
#include "global.h" // common methods
/** @defgroup uart UART peripheral used for UART communication
/** @defgroup uart USART peripheral used for UART communication
* @{
*/
#define UART_ID 1 /**< UART peripheral */
#define UART_ID 1 /**< USART peripheral */
/** @} */
#define UART_BAUDRATE 921600 /**< serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */
/* input and output ring buffer, indexes, and available memory */
static uint8_t rx_buffer[UART_BUFFER] = {0}; /**< ring buffer for received data */
static volatile uint8_t rx_buffer[UART_BUFFER] = {0}; /**< ring buffer for received data */
static volatile uint8_t rx_i = 0; /**< current position of read received data */
static volatile uint8_t rx_used = 0; /**< how much data has been received and not red */
static uint8_t tx_buffer[UART_BUFFER] = {0}; /**< ring buffer for data to transmit */
static volatile uint8_t tx_buffer[UART_BUFFER] = {0}; /**< ring buffer for data to transmit */
static volatile uint8_t tx_i = 0; /**< current position of transmitted data */
static volatile uint8_t tx_used = 0; /**< how much data needs to be transmitted */
@ -101,8 +101,8 @@ char uart_getchar(void) @@ -101,8 +101,8 @@ char uart_getchar(void)
while (!rx_used) { // idle until data is available
__WFI(); // sleep until interrupt
}
char to_return = rx_buffer[rx_i]; // get the next available character
usart_disable_rx_interrupt(USART(UART_ID)); // disable receive interrupt to prevent index corruption
volatile char to_return = rx_buffer[rx_i]; // get the next available character
rx_i = (rx_i+1)%LENGTH(rx_buffer); // update used buffer
rx_used--; // update used buffer
uart_received = (rx_used!=0); // update available data

14
lib/uart.h

@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
/** library for UART communication (API)
* @file uart.h
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016
* @date 2016-2018
* @note peripherals used: USART @ref uart
*/
#pragma once
@ -25,9 +25,9 @@ @@ -25,9 +25,9 @@
/** how many bytes available in the received buffer since last read */
extern volatile bool uart_received;
/** setup USART peripheral */
/** setup UART peripheral */
void uart_setup(void);
/** send character over USART (blocking)
/** send character over UART (blocking)
* @param[in] c character to send
* @note blocks until character transmission started */
void uart_putchar_blocking(char c);
@ -35,12 +35,12 @@ void uart_putchar_blocking(char c); @@ -35,12 +35,12 @@ void uart_putchar_blocking(char c);
* @note block until all data has been transmitted
*/
void uart_flush(void);
/** get character received over USART (blocking)
* @return character received over USART
* @note blocks until character is received over USART when received buffer is empty
/** get character received over UART (blocking)
* @return character received over UART
* @note blocks until character is received over UART when received buffer is empty
*/
char uart_getchar(void);
/** send character over USART (non-blocking)
/** send character over UART (non-blocking)
* @param[in] c character to send
* @note blocks if transmit buffer is full, else puts in buffer and returns
*/

148
lib/usart.c

@ -1,148 +0,0 @@ @@ -1,148 +0,0 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
/** library for USART communication (code)
* @file usart.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016
* @note peripherals used: USART @ref usart
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdio.h> // standard I/O facilities
#include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/usart.h> // universal synchronous asynchronous receiver transmitter library
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include "usart.h" // USART header and definitions
#include "global.h" // common methods
/** @defgroup usart USART peripheral used for UART communication
* @{
*/
#define USART_ID 1 /**< USART peripheral */
/** @} */
#define USART_BAUDRATE 921600 /**< serial baudrate, in bits per second (with 8N1 8 bits, no parity bit, 1 stop bit settings) */
/* input and output ring buffer, indexes, and available memory */
static uint8_t rx_buffer[USART_BUFFER] = {0}; /**< ring buffer for received data */
static volatile uint8_t rx_i = 0; /**< current position of read received data */
static volatile uint8_t rx_used = 0; /**< how much data has been received and not red */
static uint8_t tx_buffer[USART_BUFFER] = {0}; /**< ring buffer for data to transmit */
static volatile uint8_t tx_i = 0; /**< current position of transmitted data */
static volatile uint8_t tx_used = 0; /**< how much data needs to be transmitted */
volatile bool usart_received = false;
void usart_setup(void)
{
/* enable USART I/O peripheral */
rcc_periph_clock_enable(USART_PORT_RCC(USART_ID)); // enable clock for USART port peripheral
rcc_periph_clock_enable(USART_RCC(USART_ID)); // enable clock for USART peripheral
rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART)
gpio_set_mode(USART_PORT(USART_ID), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(USART_ID)); // setup GPIO pin USART transmit
gpio_set_mode(USART_PORT(USART_ID), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(USART_ID)); // setup GPIO pin USART receive
gpio_set(USART_PORT(USART_ID), USART_PIN_RX(USART_ID)); // pull up to avoid noise when not connected
/* setup USART parameters */
usart_set_baudrate(USART(USART_ID), USART_BAUDRATE);
usart_set_databits(USART(USART_ID), 8);
usart_set_stopbits(USART(USART_ID), USART_STOPBITS_1);
usart_set_mode(USART(USART_ID), USART_MODE_TX_RX);
usart_set_parity(USART(USART_ID), USART_PARITY_NONE);
usart_set_flow_control(USART(USART_ID), USART_FLOWCONTROL_NONE);
nvic_enable_irq(USART_IRQ(USART_ID)); // enable the USART interrupt
usart_enable_rx_interrupt(USART(USART_ID)); // enable receive interrupt
usart_enable(USART(USART_ID)); // enable USART
/* reset buffer states */
tx_i = 0;
tx_used = 0;
rx_i = 0;
rx_used = 0;
usart_received = false;
}
void usart_putchar_blocking(char c)
{
usart_flush(); // empty buffer first
usart_send_blocking(USART(USART_ID), c); // send character
}
void usart_flush(void)
{
while (tx_used) { // idle until buffer is empty
__WFI(); // sleep until interrupt
}
usart_wait_send_ready(USART(USART_ID)); // wait until transmit register is empty (transmission might not be complete)
}
char usart_getchar(void)
{
while (!rx_used) { // idle until data is available
__WFI(); // sleep until interrupt
}
char to_return = rx_buffer[rx_i]; // get the next available character
usart_disable_rx_interrupt(USART(USART_ID)); // disable receive interrupt to prevent index corruption
rx_i = (rx_i+1)%LENGTH(rx_buffer); // update used buffer
rx_used--; // update used buffer
usart_received = (rx_used!=0); // update available data
usart_enable_rx_interrupt(USART(USART_ID)); // enable receive interrupt
return to_return;
}
void usart_putchar_nonblocking(char c)
{
while (tx_used>=LENGTH(tx_buffer)) { // idle until buffer has some space
usart_enable_tx_interrupt(USART(USART_ID)); // enable transmit interrupt
__WFI(); // sleep until something happened
}
usart_disable_tx_interrupt(USART(USART_ID)); // disable transmit interrupt to prevent index corruption
tx_buffer[(tx_i+tx_used)%LENGTH(tx_buffer)] = c; // put character in buffer
tx_used++; // update used buffer
usart_enable_tx_interrupt(USART(USART_ID)); // enable transmit interrupt
}
/** USART interrupt service routine called when data has been transmitted or received */
void USART_ISR(USART_ID)(void)
{
if (usart_get_flag(USART(USART_ID), USART_SR_TXE)) { // data has been transmitted
if (!tx_used) { // no data in the buffer to transmit
usart_disable_tx_interrupt(USART(USART_ID)); // disable transmit interrupt
} else {
usart_send(USART(USART_ID),tx_buffer[tx_i]); // put data in transmit register
tx_i = (tx_i+1)%LENGTH(rx_buffer); // update location on buffer
tx_used--; // update used size
}
}
while (usart_get_flag(USART(USART_ID), USART_SR_RXNE)) { // data has been received (repeat while receiving)
char c = usart_recv(USART(USART_ID)); // save character and free USART buffer
// only save data if there is space in the buffer
if (rx_used>=LENGTH(rx_buffer)) { // if buffer is full
rx_i = (rx_i+1)%LENGTH(rx_buffer); // drop oldest data
rx_used--; // update used buffer information
}
rx_buffer[(rx_i+rx_used)%LENGTH(rx_buffer)] = c; // put character in buffer
rx_used++; // update used buffer
usart_received = true; // update available data
}
}

47
lib/usart.h

@ -1,47 +0,0 @@ @@ -1,47 +0,0 @@
/* 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 <http://www.gnu.org/licenses/>.
*
*/
/** library for USART communication (API)
* @file usart.h
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016
* @note peripherals used: USART @ref usart
*/
#pragma once
/** transmit and receive buffer sizes */
#define USART_BUFFER 128
/** how many bytes available in the received buffer since last read */
extern volatile bool usart_received;
/** setup USART peripheral */
void usart_setup(void);
/** send character over USART (blocking)
* @param[in] c character to send
* @note blocks until character transmission started */
void usart_putchar_blocking(char c);
/** ensure all data has been transmitted (blocking)
* @note block until all data has been transmitted
*/
void usart_flush(void);
/** get character received over USART (blocking)
* @return character received over USART
* @note blocks until character is received over USART when received buffer is empty
*/
char usart_getchar(void);
/** send character over USART (non-blocking)
* @param[in] c character to send
* @note blocks if transmit buffer is full, else puts in buffer and returns
*/
void usart_putchar_nonblocking(char c);
Loading…
Cancel
Save