aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/usart.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/usart.c')
-rw-r--r--src/lib/usart.c322
1 files changed, 305 insertions, 17 deletions
diff --git a/src/lib/usart.c b/src/lib/usart.c
index c1a7f43..a06690e 100644
--- a/src/lib/usart.c
+++ b/src/lib/usart.c
@@ -1,44 +1,332 @@
+/* *****************************************************************************
+ * 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/>.
+ *
+ * Created: 12/18/09 02:38:26
+ * Copyright (c) 2009 Perry L. Hung. All rights reserved.
+ *
+ * ****************************************************************************/
+
+/**
+ * @file usart.c
+ *
+ * @brief USART control routines
+ */
+
+#include "libmaple.h"
#include "stm32f10x_rcc.h"
#include "usart.h"
+#include "nvic.h"
-static inline void usart_putc(usart_port *port, uint8_t ch) {
- port->DR = ch;
+#define USART1_BASE 0x40013800
+#define USART2_BASE 0x40004400
+#define USART3_BASE 0x40004800
- /* Wait till TXE = 1 */
- while ((port->SR & USART_TXE) == 0)
- ;
+#define USART_UE BIT(13)
+#define USART_M BIT(12)
+#define USART_TE BIT(3)
+#define USART_RE BIT(2)
+#define USART_RXNEIE BIT(5) // read data register not empty interrupt enable
+#define USART_TXE BIT(7)
+#define USART_TC BIT(6)
+
+#define USART_STOP_BITS_1 BIT_MASK_SHIFT(0b0, 12)
+#define USART_STOP_BITS_05 BIT_MASK_SHIFT(0b01, 12)
+#define USART_STOP_BITS_2 BIT_MASK_SHIFT(0b02, 12)
+#define USART_STOP_BITS_15 BIT_MASK_SHIFT(0b02, 12)
+
+#define USART1_CLK 72000000UL
+#define USART2_CLK 36000000UL
+#define USART3_CLK 36000000UL
+
+#define USART_RECV_BUF_SIZE 64
+
+/* Ring buffer notes:
+ * The buffer is empty when head == tail.
+ * The buffer is full when the head is one byte in front of the tail
+ * The total buffer size must be a power of two
+ * Note, one byte is necessarily left free with this scheme */
+typedef struct usart_ring_buf {
+ uint32 head;
+ uint32 tail;
+ uint8 buf[USART_RECV_BUF_SIZE];
+} usart_ring_buf;
+
+static usart_ring_buf ring_buf1;
+static usart_ring_buf ring_buf2;
+static usart_ring_buf ring_buf3;
+
+typedef struct usart_port {
+ volatile uint32 SR; // Status register
+ volatile uint32 DR; // Data register
+ volatile uint32 BRR; // Baud rate register
+ volatile uint32 CR1; // Control register 1
+ volatile uint32 CR2; // Control register 2
+ volatile uint32 CR3; // Control register 3
+ volatile uint32 GTPR; // Guard time and prescaler register
+} usart_port;
+
+
+/* Don't overrun your buffer, seriously */
+void USART2_IRQHandler(void) {
+ /* Read the data */
+ ring_buf2.buf[ring_buf2.tail++] = (uint8_t)(((usart_port*)(USART2_BASE))->DR);
+ ring_buf2.tail %= USART_RECV_BUF_SIZE;
}
-int32_t usart_init(uint8_t usart_num) {
- ASSERT((usart_num < NR_USARTS) && (usart_num > 0));
+
+/**
+ * @brief Enable a USART in single buffer transmission mode, multibuffer
+ * receiver mode.
+ *
+ * @param usart_num USART to be initialized
+ * @param baud Baud rate to be set at
+ * @param recvBuf buf buffer for receiver
+ * @param len size of recvBuf
+ *
+ * @sideeffect Turns on the specified USART
+ */
+void usart_init(uint8 usart_num, uint32 baud) {
+ ASSERT((usart_num <= NR_USARTS) && (usart_num > 0));
+ ASSERT(baud && (baud < USART_MAX_BAUD));
+
usart_port *port;
- uint32_t clk_speed;
+ usart_ring_buf *ring_buf;
+
+ uint32 clk_speed;
+ uint32 integer_part;
+ uint32 fractional_part;
+ uint32 tmp;
switch (usart_num) {
case 1:
- port = USART1_BASE;
+ port = (usart_port*)USART1_BASE;
+ ring_buf = &ring_buf1;
clk_speed = USART1_CLK;
break;
case 2:
- port = USART2_BASE;
+ port = (usart_port*)USART2_BASE;
+ ring_buf = &ring_buf2;
clk_speed = USART2_CLK;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
break;
case 3:
- port = USART3_BASE;
+ port = (usart_port*)USART3_BASE;
+ ring_buf = &ring_buf3;
+ clk_speed = USART3_CLK;
break;
default:
/* should never get here */
ASSERT(0);
}
- uint32_t baud = 9600;
- uint32_t usartdiv = clk_speed / baud;
+
+ /* Initialize ring buffer */
+ ring_buf->head = 0;
+ ring_buf->tail = 0;
/* Set baud rate */
- port->BRR = BIT_MASK_SHIFT(B9600_MANTISSA, 4) | B9600_FRACTION;
+ integer_part = ((25 * clk_speed) / (4 * baud));
+ tmp = (integer_part / 100) << 4;
+
+ fractional_part = integer_part - (100 * (tmp >> 4));
+ tmp |= (((fractional_part * 16) + 50) / 100) & ((uint8)0x0F);
+
+ port->BRR = (uint16_t)tmp;
+
+ port->CR1 = USART_TE | // transmitter enable
+ USART_RE | // receiver enable
+ USART_RXNEIE; // receive interrupt enable
+
+ /* Turn it on in the nvic */
+ REG_SET(NVIC_ISER1, BIT(6));
+
+ /* Enable the USART and set mode to 8n1 */
+ port->CR1 |= USART_UE;
+}
+
+
+/**
+ * @brief Turn off a USART.
+ *
+ * @param USART to be disabled
+ *
+ * @sideeffect Turns off the specified USART
+ */
+void usart_disable(uint8 usart_num) {
+ ASSERT((usart_num <= NR_USARTS) && (usart_num > 0));
+ usart_port *port;
+
+ switch (usart_num) {
+ case 1:
+ port = (usart_port*)USART1_BASE;
+ break;
+ case 2:
+ port = (usart_port*)USART2_BASE;
+ break;
+ case 3:
+ port = (usart_port*)USART3_BASE;
+ break;
+ default:
+ /* should never get here */
+ ASSERT(0);
+ }
+
+ /* Is this usart enabled? */
+ if (!(port->SR & USART_UE))
+ return;
+
+ /* TC bit must be high before disabling the usart */
+ while ((port->SR & USART_TC) == 0)
+ ;
+
+ /* Disable UE */
+ port->CR1 = 0;
+}
+
+
+/**
+ * @brief Print a null terminated string to the specified USART
+ *
+ * @param[in] usart_num USART to send on
+ * @param[in] str String to send
+ */
+void usart_putstr(uint8 usart_num, const char* str) {
+ ASSERT((usart_num <= NR_USARTS) && (usart_num > 0));
+ char ch;
+
+ while((ch = *(str++)) != '\0') {
+ usart_putc(usart_num, ch);
+ }
+}
+
+/**
+ * @brief Print an unsigned integer to the specified usart
+ *
+ * @param[in] usart_num usart to send on
+ * @param[in] val number to print
+ */
+void usart_putudec(uint8 usart_num, uint32 val) {
+ ASSERT((usart_num <= NR_USARTS) && (usart_num > 0));
+ char digits[12];
+ int i;
+
+ i = 0;
+ do {
+ digits[i++] = val % 10 + '0';
+ val /= 10;
+ } while (val > 0);
+ while (--i >= 0) {
+ usart_putc(usart_num, digits[i]);
+ }
+
+}
- /* Enable the USART and set 8n1 (M bit clear) enable transmitter*/
- port->CR1 = USART_UE | USART_TE;
- return 0;
+/**
+ * @brief Return one character from the receive buffer. Assumes
+ * that there is data available.
+ *
+ * @param[in] usart_num number of the usart to read from
+ *
+ * @return character from ring buffer
+ *
+ * @sideeffect may update the head pointer of the recv buffer
+ */
+uint8 usart_getc(uint8 usart_num) {
+ uint8 ch;
+ usart_ring_buf *rb;
+
+ switch (usart_num) {
+ case 1:
+ rb = &ring_buf1;
+ break;
+ case 2:
+ rb = &ring_buf2;
+ break;
+ case 3:
+ rb = &ring_buf3;
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ /* Make sure there's actually data to be read */
+ ASSERT(rb->head != rb->tail);
+
+ /* Read the data and check for wraparound */
+ ch = rb->buf[rb->head++];
+ rb->head %= USART_RECV_BUF_SIZE;
+
+ return ch;
}
+
+uint32 usart_data_available(uint8 usart_num) {
+ usart_ring_buf *rb;
+
+ switch (usart_num) {
+ case 1:
+ rb = &ring_buf1;
+ break;
+ case 2:
+ rb = &ring_buf2;
+ break;
+ case 3:
+ rb = &ring_buf3;
+ break;
+ default:
+ ASSERT(0);
+ }
+
+ return rb->tail - rb->head;
+}
+
+
+
+/**
+ * @brief Output a character out the uart
+ *
+ * @param[in] usart_num usart number to output on
+ * @param[in] ch character to send
+ *
+ */
+void usart_putc(uint8 usart_num, uint8 ch) {
+ ASSERT((usart_num <= NR_USARTS) && (usart_num > 0));
+ usart_port *port;
+
+ switch (usart_num) {
+ case 1:
+ port = (usart_port*)USART1_BASE;
+ break;
+ case 2:
+ port = (usart_port*)USART2_BASE;
+ break;
+ case 3:
+ port = (usart_port*)USART3_BASE;
+ break;
+ default:
+ /* Should never get here */
+ ASSERT(0);
+ }
+
+ if (ch == '\n') {
+ usart_putc(usart_num, '\r');
+ }
+
+ port->DR = ch;
+
+ /* Wait for transmission to complete */
+ while ((port->SR & USART_TXE) == 0)
+ ;
+}
+
+