⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial.c

📁 * The functions debug_init() and debug() implement non-RTOS * serial port initialization and a blo
💻 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 + -