kendryte-freertos-sdk/lib/bsp/device/uart.c

201 lines
5.2 KiB
C

/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <FreeRTOS.h>
#include <driver.h>
#include <hal.h>
#include <plic.h>
#include <semphr.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysctl.h>
#include <uart.h>
#define UART_BRATE_CONST 16
#define RINGBUFF_LEN 64
#define COMMON_ENTRY \
uart_data* data = (uart_data*)userdata; \
volatile uart_t* uart = (volatile uart_t*)data->base_addr; \
(void)uart;
typedef struct
{
size_t head;
size_t tail;
size_t length;
char ring_buffer[RINGBUFF_LEN];
} ringbuffer_t;
typedef struct
{
enum sysctl_clock_e clock;
uintptr_t base_addr;
size_t channel;
ringbuffer_t* recBuf;
} uart_data;
static int write_ringbuff(uint8_t rdata, void* userdata)
{
COMMON_ENTRY;
ringbuffer_t* ring_buff = data->recBuf;
if (ring_buff->length >= RINGBUFF_LEN)
{
return -1;
}
ring_buff->ring_buffer[ring_buff->tail] = rdata;
ring_buff->tail = (ring_buff->tail + 1) % RINGBUFF_LEN;
ring_buff->length++;
return 0;
}
static int read_ringbuff(char* rData, size_t len, void* userdata)
{
COMMON_ENTRY;
ringbuffer_t* ring_buff = data->recBuf;
size_t cnt = 0;
while ((len--) && ring_buff->length)
{
*(rData++) = ring_buff->ring_buffer[ring_buff->head];
ring_buff->head = (ring_buff->head + 1) % RINGBUFF_LEN;
ring_buff->length--;
cnt++;
}
return cnt;
}
static void on_irq_apbuart_recv(void* userdata)
{
COMMON_ENTRY;
while (uart->LSR & 1)
{
write_ringbuff(((uint8_t)(uart->RBR & 0xff)), userdata);
}
}
static void uart_install(void* userdata)
{
COMMON_ENTRY;
sysctl_clock_enable(data->clock);
}
static int uart_open(void* userdata)
{
COMMON_ENTRY;
ringbuffer_t* ring_buff = malloc(sizeof(ringbuffer_t));
ring_buff->head = 0;
ring_buff->tail = 0;
ring_buff->length = 0;
data->recBuf = ring_buff;
pic_set_irq_handler(IRQN_UART1_INTERRUPT + data->channel, on_irq_apbuart_recv, userdata);
pic_set_irq_priority(IRQN_UART1_INTERRUPT + data->channel, 1);
pic_set_irq_enable(IRQN_UART1_INTERRUPT + data->channel, 1);
return 1;
}
static void uart_close(void* userdata)
{
COMMON_ENTRY;
free(data->recBuf);
}
static void uart_config(size_t baud_rate, size_t data_width, uart_stopbit stopbit, uart_parity parity, void* userdata)
{
COMMON_ENTRY;
configASSERT(data_width >= 5 && data_width <= 8);
if (data_width == 5)
{
configASSERT(stopbit != UART_STOP_2);
}
else
{
configASSERT(stopbit != UART_STOP_1_5);
}
uint32_t stopbit_val = stopbit == UART_STOP_1 ? 0 : 1;
uint32_t parity_val = 0;
switch (parity)
{
case UART_PARITY_NONE:
parity_val = 0;
break;
case UART_PARITY_ODD:
parity_val = 1;
break;
case UART_PARITY_EVEN:
parity_val = 3;
break;
default:
configASSERT(!"Invalid parity");
break;
}
uint32_t freq = sysctl_clock_get_freq(data->clock);
uint32_t u16Divider = (freq + UART_BRATE_CONST * baud_rate / 2) / (UART_BRATE_CONST * baud_rate);
/* Set UART registers */
uart->TCR &= ~(1u);
uart->TCR &= ~(1u << 3);
uart->TCR &= ~(1u << 4);
uart->TCR |= (1u << 2);
uart->TCR &= ~(1u << 1);
uart->DE_EN &= ~(1u);
uart->LCR |= 1u << 7;
uart->DLL = u16Divider & 0xFF;
uart->DLH = u16Divider >> 8;
uart->LCR = 0;
uart->LCR = (data_width - 5) | (stopbit_val << 2) | (parity_val << 3);
uart->LCR &= ~(1u << 7);
uart->MCR &= ~3;
uart->IER = 1;
}
static int uart_putc(volatile uart_t* uart, char c)
{
while (!(uart->LSR & (1u << 6)))
continue;
uart->THR = c;
return 0;
}
static int uart_read(char* buffer, size_t len, void* userdata)
{
return read_ringbuff(buffer, len, userdata);
}
static int uart_write(const char* buffer, size_t len, void* userdata)
{
COMMON_ENTRY;
int write = 0;
while (write < len)
{
uart_putc(uart, *buffer++);
write++;
}
return write;
}
static uart_data dev0_data = {SYSCTL_CLOCK_UART1, UART1_BASE_ADDR, 0, NULL};
static uart_data dev1_data = {SYSCTL_CLOCK_UART2, UART2_BASE_ADDR, 1, NULL};
static uart_data dev2_data = {SYSCTL_CLOCK_UART3, UART3_BASE_ADDR, 2, NULL};
const uart_driver_t g_uart_driver_uart0 = {{&dev0_data, uart_install, uart_open, uart_close}, uart_config, uart_read, uart_write};
const uart_driver_t g_uart_driver_uart1 = {{&dev1_data, uart_install, uart_open, uart_close}, uart_config, uart_read, uart_write};
const uart_driver_t g_uart_driver_uart2 = {{&dev2_data, uart_install, uart_open, uart_close}, uart_config, uart_read, uart_write};