diff options
author | iperry <iperry@749a229e-a60e-11de-b98f-4500b42dc123> | 2009-12-19 10:53:07 +0000 |
---|---|---|
committer | iperry <iperry@749a229e-a60e-11de-b98f-4500b42dc123> | 2009-12-19 10:53:07 +0000 |
commit | acf59b1abb346998c492b93fee4a680a32f538d5 (patch) | |
tree | c49dd57f489769608095736457a0db9350868a47 /src/lib/usart.c | |
parent | af4c4985cef82b80b936584d686c80d9538082b6 (diff) | |
download | librambutan-acf59b1abb346998c492b93fee4a680a32f538d5.tar.gz librambutan-acf59b1abb346998c492b93fee4a680a32f538d5.zip |
Added licensing. Moved lots of header files around. Added HardwareSerial reads, writes. Tweaked some of the util and assert functions. Added various useful routines for printing stuff to the serial port. Continued moving out stm32lib. Slightly more consistent naming, this will come in another change.
git-svn-id: https://leaflabs.googlecode.com/svn/trunk/library@74 749a229e-a60e-11de-b98f-4500b42dc123
Diffstat (limited to 'src/lib/usart.c')
-rw-r--r-- | src/lib/usart.c | 322 |
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) + ; +} + + |