parent
0c925ba957
commit
e14f6bccd6
@ -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
|
||||
} |
||||
} |
||||
} |
||||
|
@ -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); |
@ -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
|
||||
} |
||||
} |
@ -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…
Reference in new issue