LPCXpresso55S69_MRuby/src/app_syscalls.c

313 lines
8.3 KiB
C

#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
/* Board */
#include "pin_mux.h"
/* SDK drivers */
#include "fsl_power.h"
#include "fsl_rtc.h"
#include "fsl_usart.h"
/* FreeRTOS */
#include "FreeRTOS.h"
#include "event_groups.h"
#include "semphr.h"
#include "task.h"
#define APP_STDIO_UART_ID 0
#define APP_STDIO_UART_INSTANCE USART0
#define APP_STDIO_UART_CLOCK_ATTACH kFRO12M_to_FLEXCOMM0
#define APP_STDIO_UART_TX_FIFO_SIZE 16
#define APP_STDIO_UART_RX_FIFO_SIZE 16
#define APP_STDIO_UART_TX_BEAT_SIZE (APP_STDIO_UART_TX_FIFO_SIZE - 1)
#define APP_STDIO_UART_RX_BEAT_SIZE (APP_STDIO_UART_RX_FIFO_SIZE - 1)
#define APP_STDIO_UART_IRQ_NR FLEXCOMM0_IRQn
#define APP_STDIO_UART_IRQ_PRIO (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + 1)
#define APP_STDIO_UART_IRQ_HANDLER FLEXCOMM0_IRQHandler
#define APP_STDIO_UART_EVENT_TX_DONE (1 << 0U)
#define APP_STDIO_UART_EVENT_RX_DONE (1 << 1U)
#define APP_STDIO_UART_EVENT_ERROR (1 << 2U)
#define APP_TIME_RTC_INIT_RETRIES 32
#define APP_TIME_RTC_OFFSET_EPOCH (1612137600ULL) /* Magic number, don't ask. */
static SemaphoreHandle_t s_malloc_lock = NULL;
static SemaphoreHandle_t s_stdio_tx_lock = NULL;
static SemaphoreHandle_t s_stdio_rx_lock = NULL;
static EventGroupHandle_t s_stdio_event = NULL;
/* ========== Internal Use Functions ========== */
static int app_stdio_init(void) {
int ret = 0;
s_stdio_tx_lock = xSemaphoreCreateMutex();
if (s_stdio_tx_lock == NULL) {
return -1;
}
s_stdio_rx_lock = xSemaphoreCreateMutex();
if (s_stdio_rx_lock == NULL) {
ret = -1;
goto free_tx_sem_exit;
}
s_stdio_event = xEventGroupCreate();
if (s_stdio_event == NULL) {
ret = -1;
goto free_rx_sem_exit;
}
CLOCK_AttachClk(APP_STDIO_UART_CLOCK_ATTACH);
usart_config_t usart_config;
USART_GetDefaultConfig(&usart_config);
usart_config.baudRate_Bps = 115200U;
usart_config.txWatermark = 0U;
usart_config.rxWatermark = APP_STDIO_UART_RX_BEAT_SIZE;
usart_config.enableTx = true;
usart_config.enableRx = true;
usart_config.bitCountPerChar = kUSART_8BitsPerChar;
usart_config.stopBitCount = kUSART_OneStopBit;
usart_config.parityMode = kUSART_ParityDisabled;
uint32_t stdio_fc_clk_freq = CLOCK_GetFlexCommClkFreq(APP_STDIO_UART_ID);
if (USART_Init(APP_STDIO_UART_INSTANCE, &usart_config, stdio_fc_clk_freq) != kStatus_Success) {
ret = -1;
goto free_event_exit;
}
USART_SetTxFifoWatermark(APP_STDIO_UART_INSTANCE, 0); /* Has to be set to 0 to detect TX done condition */
USART_SetRxFifoWatermark(APP_STDIO_UART_INSTANCE, APP_STDIO_UART_RX_BEAT_SIZE); /* Might be adjusted on-the-fly */
NVIC_SetPriority(APP_STDIO_UART_IRQ_NR, APP_STDIO_UART_IRQ_PRIO);
EnableIRQ(APP_STDIO_UART_IRQ_NR);
return ret;
free_event_exit:
vEventGroupDelete(s_stdio_event);
free_rx_sem_exit:
vSemaphoreDelete(s_stdio_rx_lock);
free_tx_sem_exit:
vSemaphoreDelete(s_stdio_tx_lock);
return ret;
}
void APP_STDIO_UART_IRQ_HANDLER(void) {
uint32_t event_flag = 0UL;
uint32_t interrupts_enabled = USART_GetEnabledInterrupts(APP_STDIO_UART_INSTANCE);
uint32_t fifo_int_status = APP_STDIO_UART_INSTANCE->FIFOINTSTAT;
USART_ClearStatusFlags(APP_STDIO_UART_INSTANCE, kUSART_AllClearFlags);
if ((interrupts_enabled & kUSART_TxLevelInterruptEnable) && (fifo_int_status & USART_FIFOINTSTAT_TXLVL_MASK)) {
USART_DisableInterrupts(APP_STDIO_UART_INSTANCE, kUSART_TxLevelInterruptEnable);
event_flag |= APP_STDIO_UART_EVENT_TX_DONE;
}
if ((interrupts_enabled & kUSART_RxLevelInterruptEnable) && (fifo_int_status & USART_FIFOINTSTAT_RXLVL_MASK)) {
USART_DisableInterrupts(APP_STDIO_UART_INSTANCE, kUSART_RxLevelInterruptEnable);
event_flag |= APP_STDIO_UART_EVENT_RX_DONE;
}
BaseType_t higher_prio_task_woken = pdFALSE;
xEventGroupSetBitsFromISR(s_stdio_event, event_flag, &higher_prio_task_woken);
portYIELD_FROM_ISR(higher_prio_task_woken);
}
static int app_rtc_time_init(void) {
int ret = 0;
POWER_DisablePD(kPDRUNCFG_PD_XTAL32K);
POWER_EnablePD(kPDRUNCFG_PD_FRO32K);
CLOCK_AttachClk(kXTAL32K_to_OSC32K);
RTC_Init(RTC);
RTC_EnableTimer(RTC, true);
RTC_EnableWakeupTimer(RTC, true);
/* Enable sub second counter */
RTC->CTRL |= RTC_CTRL_RTC_SUBSEC_ENA_MASK;
CLOCK_SetRtc1hzClkDiv(32768U);
uint32_t rtc_sec = RTC_GetSecondsTimerCount(RTC);
uint8_t rtc_retries = 0U;
do {
SDK_DelayAtLeastUs(120 * 1000, CLOCK_GetCoreSysClkFreq());
rtc_retries++;
if (rtc_retries >= APP_TIME_RTC_INIT_RETRIES) {
ret = -1;
break;
}
} while (rtc_sec == RTC_GetSecondsTimerCount(RTC));
return ret;
}
int app_syscalls_init(void) {
s_malloc_lock = xSemaphoreCreateRecursiveMutex();
if (s_malloc_lock == NULL) {
return -1;
}
if (app_stdio_init() != 0) {
return -2;
}
if (app_rtc_time_init() != 0) {
return -3;
}
return 0;
}
void __malloc_lock(struct _reent *reent) {
xSemaphoreTakeRecursive(s_malloc_lock, portMAX_DELAY);
}
void __malloc_unlock(struct _reent *reent) {
xSemaphoreGiveRecursive(s_malloc_lock);
}
int _read(int file, char *ptr, int len) {
if (file != STDIN_FILENO) {
return 0;
}
xSemaphoreTake(s_stdio_rx_lock, portMAX_DELAY);
USART_SetRxFifoWatermark(APP_STDIO_UART_INSTANCE, 0);
USART_EnableInterrupts(APP_STDIO_UART_INSTANCE, kUSART_RxLevelInterruptEnable);
xEventGroupWaitBits(s_stdio_event, APP_STDIO_UART_EVENT_RX_DONE, pdTRUE, pdFALSE, portMAX_DELAY);
uint8_t rx_level = USART_GetRxFifoCount(APP_STDIO_UART_INSTANCE);
if (rx_level > len) {
rx_level = len;
}
for (uint8_t i = 0; i < rx_level; i++) {
ptr[i] = USART_ReadByte(APP_STDIO_UART_INSTANCE);
}
xSemaphoreGive(s_stdio_rx_lock);
return rx_level;
}
int _write(int file, char *buf, int len) {
if (file != STDOUT_FILENO && file != STDERR_FILENO) {
return 0;
}
xSemaphoreTake(s_stdio_tx_lock, portMAX_DELAY);
int xfer_count = len / APP_STDIO_UART_TX_BEAT_SIZE;
int bytes_remaining = len % APP_STDIO_UART_TX_BEAT_SIZE;
if (bytes_remaining) {
xfer_count++;
}
for (int i = 0; i < xfer_count; i++) {
uint32_t tx_bytes = APP_STDIO_UART_TX_BEAT_SIZE;
if (i == xfer_count - 1) {
if (bytes_remaining) {
tx_bytes = bytes_remaining;
}
}
/* Hold TX operation before enabling level interrupt, otherwise a level interrupt will fire instantly */
APP_STDIO_UART_INSTANCE->CTL |= USART_CTL_TXDIS_MASK;
/* Fill FIFO */
for (uint32_t j = 0; j < tx_bytes; j++) {
USART_WriteByte(APP_STDIO_UART_INSTANCE, buf[i * APP_STDIO_UART_TX_BEAT_SIZE + j]);
}
USART_EnableInterrupts(APP_STDIO_UART_INSTANCE, kUSART_TxLevelInterruptEnable);
/* Begin TX transfer */
APP_STDIO_UART_INSTANCE->CTL &= ~(USART_CTL_TXDIS_MASK);
/* Wait for TX complete */
xEventGroupWaitBits(s_stdio_event, APP_STDIO_UART_EVENT_TX_DONE, pdTRUE, pdFALSE, portMAX_DELAY);
}
xSemaphoreGive(s_stdio_tx_lock);
return len;
}
int _open(const char *path, int flags, ...) {
return -1;
}
int _close(int file) {
errno = -EBADF;
return -1;
}
int _fstat(int file, struct stat *st) {
if ((STDOUT_FILENO == file) || (STDERR_FILENO == file)) {
st->st_mode = S_IFCHR;
return 0;
} else {
errno = EBADF;
return -1;
}
}
int _gettimeofday(struct timeval *ptimeval, void *ptimezone) {
ptimeval->tv_sec = APP_TIME_RTC_OFFSET_EPOCH + RTC_GetSecondsTimerCount(RTC);
ptimeval->tv_usec = RTC->SUBSEC * 1000000 / 32768;
return 0;
}
int _getpid(void) {
return 1;
}
int _isatty(int file) {
if (file == STDIN_FILENO || file == STDOUT_FILENO || file == STDERR_FILENO) {
return 1;
}
return 0;
}
int _kill(int pid, int sig) {
errno = -EINVAL;
return -1;
}
int _lseek(int file, int offset, int whence) {
return 0;
}
unsigned int sleep(unsigned int sec) {
vTaskDelay(pdMS_TO_TICKS(sec * 1000));
return 0;
}
int usleep(useconds_t us) {
vTaskDelay(pdMS_TO_TICKS(us / 1000));
return 0;
}