📄 serial.c
字号:
/**
* \file serial.c
* \brief Serial port API
*
* The functions debug_init() and debug() implement non-RTOS
* serial port initialization and a blocked debug output. The
* debug() function can be used in 'error message and die'
* situations.
*/
#include "includes.h"
#include "serial.h"
#include "ring_buffer.h"
/* ----------------------------------------------------------------
* Serial port debug interface
* ----------------------------------------------------------------
*/
void debug_init()
{
static int init = 0;
if (init != 0) {
return;
}
init = 1;
/* Pin function: UART1 Tx/Rx (p82 Table 50, User Manual) */
PINSEL0 = (1<<16) | (1 << 18);
/* UART1 configuration: Ch 9 pp101-114 User Manual */
/* 8 bits, no Parity, 1 Stop bit, and set DLAB = 1 */
U1LCR = 0x83;
/* 9600 Baud Rate @ 15MHz VPB Clock (15MHz/(16x9600) = 97) */
U1DLL = 97;
U1DLM = 0;
/* Clear DLAB = 0 */
U1LCR = 0x03;
}
void debug(char *msg)
{
int i;
int len = strlen(msg);
for(i = 0; i < len; i++) {
while (!(U1LSR & 0x20));
U1THR = msg[i];
}
}
/* ----------------------------------------------------------------
* Serial port RTOS driver
* ----------------------------------------------------------------
*/
/* Serial port device driver state */
typedef struct {
uint32_t tx_busy;
ring_buffer_t tx;
OS_EVENT *tx_sem;
ring_buffer_t rx;
OS_EVENT *rx_sem;
} serial_port_t;
/* UART1 */
#define SERIAL_TXBUFSIZE 256
#define SERIAL_RXBUFSIZE 256
static uint8_t uart1_txdata[SERIAL_TXBUFSIZE];
static uint8_t uart1_rxdata[SERIAL_RXBUFSIZE];
static serial_port_t uart1;
/* ISR */
static void serial_isr(void);
int32_t serial_init()
{
OS_CPU_SR cpu_sr;
debug_init();
/* uCOS-II semaphores */
uart1.tx_sem = OSSemCreate(SERIAL_TXBUFSIZE);
if (uart1.tx_sem == 0) {
debug("Transmit semaphore creation failed.\r\n");
return -1;
}
uart1.rx_sem = OSSemCreate(0);
if (uart1.rx_sem == 0) {
debug("Receive semaphore creation failed.\r\n");
return -1;
}
/* Ring-buffers */
ring_buffer_init(
SERIAL_TXBUFSIZE, uart1_txdata, &uart1.tx);
ring_buffer_init(
SERIAL_RXBUFSIZE, uart1_rxdata, &uart1.rx);
/* Transmitter busy flag */
uart1.tx_busy = 0;
OS_ENTER_CRITICAL();
/* Setup the UART interrupt sources
* - the receive interrupt needs to be enabled here
* - the transmit interrupt is enabled later
*/
U1IER = 0x01; /* RBR interrupt enable */
/* VIC setup */
/* Write the UART1 handler address to the VIC
* - UART1 is interrupt source 7, p68 User Manual
* - use priority slot 1
*/
VICVectAddr1 = (unsigned long)serial_isr;
VICVectCntl1 = 0x20 | 7;
/* Setup the VIC to enable UART1 to generate IRQ */
VICIntSelect &= ~(1 << 7); /* Clear bit 7 to select IRQ */
VICIntEnable = (1 << 7); /* Set bit to to enable */
OS_EXIT_CRITICAL();
return 0;
}
void serial_isr(void)
{
uint32_t source;
uint32_t status;
int32_t ch;
/* Determine UART1 interrupt source */
source = U1IIR & 0xF;
if (source & 1) {
/* Spurious */
return;
}
source >>= 1;
switch (source) {
case 1:
/* Transmit holding register empty */
/* (read of U1IIR above clears the interrupt) */
ch = ring_buffer_remove(&uart1.tx);
if (ch >= 0) {
U1THR = ch;
OSSemPost(uart1.tx_sem);
} else {
/* Empty */
uart1.tx_busy = 0;
/* Disable interrupt */
U1IER &= ~0x02; /* THRE interrupt disable */
}
break;
case 2:
/* Receive data available */
ch = U1RBR; /* read char/clear intr */
ring_buffer_add(&uart1.rx, ch);
OSSemPost(uart1.rx_sem);
break;
case 3:
/* Receive line status (error) */
status = U1LSR; /* read/clear it */
break;
case 6:
/* Character timeout (a received character is available) */
ch = U1RBR; /* read char/clear intr */
ring_buffer_add(&uart1.rx, ch);
OSSemPost(uart1.rx_sem);
break;
default:
break;
}
return;
}
/* Read a character from the serial port */
int32_t serial_getchar()
{
OS_CPU_SR cpu_sr;
INT8U status;
int32_t ch;
/* Wait for a character in the receive buffer */
OSSemPend(uart1.rx_sem, 0, &status);
if (status != OS_NO_ERR) {
return -1;
}
/* Get the character from the receive buffer */
OS_ENTER_CRITICAL();
ch = ring_buffer_remove(&uart1.rx);
OS_EXIT_CRITICAL();
return ch;
}
/* The following code first checks if there is space
* in the transmit buffer, and then once inside the
* critical section determines whether to buffer or
* write directly to the transmitter. This sequence
* is required, since the test for a busy transmitter
* and buffering of a char if it is, must occur within
* a single critical section.
*
* If the flag was checked in a critical section, and
* if it were busy, the critical section exited and the
* semaphore pended on, an interrupt can occur and clear
* the flag (a delay loop was used to test this case).
* This would leave chars in the buffer that were not
* transmitted until the next putchar, and then the
* characters are transmitted out of sequence.
*/
int32_t serial_putchar(int8_t ch)
{
INT8U status;
OS_CPU_SR cpu_sr;
/* Check for space in the transmit buffer */
OSSemPend(uart1.tx_sem, 0, &status);
if (status != OS_NO_ERR) {
/* No character written */
return -1;
}
/* Check the serial port transmitter status */
OS_ENTER_CRITICAL();
if (!uart1.tx_busy) {
/* Transmitter was not busy */
uart1.tx_busy = 1;
/* Transmit char (clears THRE interrupt) */
U1THR = ch;
/* Enable transmit interrupts */
U1IER |= 0x02; /* THRE interrupt enable */
/* We did not use the buffer */
OSSemPost(uart1.tx_sem);
} else {
/* Put the character in the transmit buffer */
ring_buffer_add(&uart1.tx, ch);
}
OS_EXIT_CRITICAL();
return 0;
}
/* This could be made to have less overhead by accessing
* the ring buffer directly, i.e., if busy put one char
* in the transmitter and the rest in the ring buffer,
* or all in the ring buffer.
*/
int32_t serial_puts(int8_t *msg)
{
int32_t status = 0;
uint32_t i, len;
len = strlen(msg);
for (i = 0; i < len; i++) {
status = serial_putchar(msg[i]);
if (status < 0) {
break;
}
}
return status;
}
/* Read a string */
int32_t serial_gets(int8_t *msg, uint32_t max)
{
int32_t ch;
uint32_t len = 0;
while ((len+1) != max) {
/* Read the next char */
ch = serial_getchar();
if (ch < 0) {
msg[len++] = '\0';/* terminate the string */
return len;
}
/* Check for termination character(s) */
switch(ch) {
case '\r':
case '\n':
msg[len++] = '\0';/* terminate the string */
return len;
case 0x08:
len--;
break; /* backspace key */
default:
msg[len++] = ch;
break;
}
}
/* (max-1) reached */
msg[max-1] = '\0';
return max;
}
/* Serial port configuration */
int32_t serial_config(
uint32_t baud_rate,
uint32_t data_width,
uint32_t stop_bits,
uint32_t parity)
{
OS_CPU_SR cpu_sr;
uint32_t baud_div;
uint32_t lcr;
/* Baud rate divisor */
baud_div = (uint32_t)(SERIAL_CLOCK/baud_rate);
/* Check the data width requested */
switch (data_width) {
case 5:
case 6:
case 7:
case 8:
break;
default:
return -1;
}
/* lcr[1:0] */
lcr = data_width - 5;
/* Check the stop bits requested */
switch (stop_bits) {
case 1:
case 2:
break;
default:
return -1;
}
/* lcr[2] */
lcr |= (stop_bits - 1) << 2;
/* Check the parity requested */
switch (parity) {
case SERIAL_PARITY_NONE:
break;
case SERIAL_PARITY_ODD:
/* Enable parity: lcr[3] = 1
* Odd parity: lcr[5:4] = 0
*/
lcr |= (1 << 3);
break;
case SERIAL_PARITY_EVEN:
/* Enable parity: lcr[3] = 1
* Even parity: lcr[5:4] = 1
*/
lcr |= (1 << 3) | (1 << 4);
break;
case SERIAL_PARITY_FORCE_ONE:
/* Enable parity: lcr[3] = 1
* Force "1": lcr[5:4] = 2
*/
lcr |= (1 << 3) | (2 << 4);
break;
case SERIAL_PARITY_FORCE_ZERO:
/* Enable parity: lcr[3] = 1
* Force "0": lcr[5:4] = 3
*/
lcr |= (1 << 3) | (3 << 4);
break;
default:
return -1;
}
/* UART1 configuration: Ch 9 pp101-114 User Manual */
OS_ENTER_CRITICAL();
/* Program the line control register, and set DLAB = 1 */
U1LCR = (1 << 7) | lcr;
/* Baud Rate Divisor */
U1DLL = baud_div;
U1DLM = baud_div >> 8;
/* Clear DLAB = 0 */
U1LCR = lcr;
OS_EXIT_CRITICAL();
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -