/* Copyright 2017 fishpepper gmail.com 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 . author: fishpepper gmail.com */ #include "cc2510fx.h" #include "hal_cc25xx.h" #include "hal_uart.h" #include "hal_defines.h" #include "hal_delay.h" #include "config.h" #include "hal_dma.h" #include "uart.h" #include "debug.h" #include "wdt.h" #include "delay.h" #include "led.h" void hal_uart_init(void) { EXTERNAL_MEMORY union hal_uart_config_t sbus_uart_config; #if SBUS_UART == USART0_P1 // -> USART0_P1 // use ALT2 -> Set flag -> P1_5 = TX / P1_4 = RX PERCFG |= (PERCFG_U0CFG); // configure pins as peripheral: P1SEL |= (1<<5) | (1<<4); // make sure all P0 pins switch to normal GPIO P0SEL &= ~(0x3C); // make tx pin output: P1DIR |= (1<<5); #elif SBUS_UART == USART1_P0 // USART1 use ALT1 -> Clear flag -> Port P0_4 = TX PERCFG &= ~(PERCFG_U1CFG); // USART1 has priority when USART0 is also enabled P2DIR = (P2DIR & 0x3F) | 0b01000000; // configure pin P0_4 (TX) and P0_5 (RX) as special function: P0SEL |= (1<<4) | (1<<5); // make sure all P1 pins switch to normal GPIO // P1SEL &= ~(0xF0); // make tx pin output: P0DIR |= (1<<4); #elif SBUS_UART == USART1_P1 // USART1 use ALT2 -> SET flag -> Port P1_6 = TX PERCFG |= (PERCFG_U1CFG); // USART1 has priority when USART0 is also enabled P2DIR = (P2DIR & 0x3F) | 0b01000000; // configure pin P1_6 (TX) and P1_7(RX) as special function: P1SEL |= (1<<6) | (1<<7); // make tx pin output: P1DIR |= (1<<6); #else #error "UNSUPPORTED UART" #endif // SBUS_UART == ... // set baudrate #if (SBUS_UART == USART0_P1) || (SBUS_UART == USART0_P0) U0BAUD = CC2510_BAUD_M_100000; U0GCR = (U0GCR & ~0x1F) | (CC2510_BAUD_E_100000); #else U1BAUD = CC2510_BAUD_M_100000; U1GCR = (U1GCR & ~0x1F) | (CC2510_BAUD_E_100000); #endif // SBUS_UART == ... // set up config for USART -> 8E2 #ifdef SBUS_INVERTED // this is a really nice feature of the cc2510: // we can invert the idle level of the usart // by setting STOP to zero. by inverting // the parity, the startbit, and the data // by using the SBUS_PREPARE_DATA() macro // we can effectively invert the usart in software :) sbus_uart_config.bit.START = 1; // startbit level = low sbus_uart_config.bit.STOP = 0; // stopbit level = high sbus_uart_config.bit.D9 = 1; // UNEven parity #else // standard usart, non-inverted mode // NOTE: most sbus implementations use inverted mode sbus_uart_config.bit.START = 0; // startbit level = low sbus_uart_config.bit.STOP = 1; // stopbit level = high sbus_uart_config.bit.D9 = 0; // Even parity #endif // SBUS_INVERTED sbus_uart_config.bit.SPB = 1; // 1 = 2 stopbits sbus_uart_config.bit.PARITY = 1; // 1 = parity enabled, D9=0 -> even parity sbus_uart_config.bit.BIT9 = 1; // 8bit sbus_uart_config.bit.FLOW = 0; // no hw flow control sbus_uart_config.bit.ORDER = 0; // lsb first // activate uart config hal_uart_set_mode(&sbus_uart_config); // use dma channel 3 for transmission: hal_dma_config[3].PRIORITY = DMA_PRI_LOW; hal_dma_config[3].M8 = DMA_M8_USE_7_BITS; hal_dma_config[3].IRQMASK = DMA_IRQMASK_DISABLE; #if (SBUS_UART == USART0_P1) || (SBUS_UART == USART0_P0) hal_dma_config[3].TRIG = DMA_TRIG_UTX0; #else hal_dma_config[3].TRIG = DMA_TRIG_UTX1; #endif // SBUS_UART == ... hal_dma_config[3].TMODE = DMA_TMODE_SINGLE; hal_dma_config[3].WORDSIZE = DMA_WORDSIZE_BYTE; // source address will be set during tx start SET_WORD(hal_dma_config[3].SRCADDRH, hal_dma_config[3].SRCADDRL, 0); #if (SBUS_UART == USART0_P1) || (SBUS_UART == USART0_P0) SET_WORD(hal_dma_config[3].DESTADDRH, hal_dma_config[3].DESTADDRL, &X_U0DBUF); #else SET_WORD(hal_dma_config[3].DESTADDRH, hal_dma_config[3].DESTADDRL, &X_U1DBUF); #endif // SBUS_UART == ... hal_dma_config[3].VLEN = DMA_VLEN_USE_LEN; // len will be set during tx start SET_WORD(hal_dma_config[3].LENH, hal_dma_config[3].LENL, 0); // configure src and dest increments hal_dma_config[3].SRCINC = DMA_SRCINC_1; hal_dma_config[3].DESTINC = DMA_DESTINC_0; // set pointer to the DMA configuration struct into DMA-channel 1-4 // configuration, should have happened in adc.c already... SET_WORD(DMA1CFGH, DMA1CFGL, &hal_dma_config[1]); // arm the relevant DMA channel for UART TX, and apply 45 NOP's // to allow the DMA configuration to load // -> do a sleep instead of those nops... DMAARM |= DMA_ARM_CH3; hal_delay_45nop(); #ifdef HUB_TELEMETRY_ON_SBUS_UART // activate serial rx interrupt #if (SBUS_UART == USART0_P1) || (SBUS_UART == USART0_P0) URX0IF = 0; // enable receiption U0CSR |= UxCSR_RX_ENABLE; // enable RX interrupt URX0IE = 1; #else URX1IF = 0; // enable receiption U1CSR |= UxCSR_RX_ENABLE; // enable RX interrupt URX1IE = 1; #endif // HUB_TELEMETRY_ON_SBUS_UART // enable global ints EA = 1; #endif // SBUS_UART == ... } static void hal_uart_set_mode(EXTERNAL_MEMORY union hal_uart_config_t *cfg) { #if (SBUS_UART == USART0_P1) || (SBUS_UART == USART0_P0) // enable uart mode U0CSR |= 0x80; // store config to U1UCR register U0UCR = cfg->byte & (0x7F); // store config to U1GCR: (msb/lsb) if (cfg->bit.ORDER) { U0GCR |= U0GCR_ORDER; } else { U0GCR &= ~U0GCR_ORDER; } // interrupt prio to 1 (0..3=highest) IP0 |= (1<<2); IP1 &= ~(1<<2); #else // enable uart mode U1CSR |= 0x80; // store config to U1UCR register U1UCR = cfg->byte & (0x7F); // store config to U1GCR: (msb/lsb) if (cfg->bit.ORDER) { U1GCR |= U1GCR_ORDER; } else { U1GCR &= ~U1GCR_ORDER; } // interrupt prio to 1 (0..3=highest) IP0 |= (1<<3); IP1 &= ~(1<<3); #endif // SBUS_UART } void hal_uart_start_transmission(uint8_t *data, uint8_t len) { // important: src addr start is data[1] SET_WORD(hal_dma_config[3].SRCADDRH, hal_dma_config[3].SRCADDRL, &data[1]); // configure length of transfer SET_WORD(hal_dma_config[3].LENH, hal_dma_config[3].LENL, len); // time to send this frame! // re-arm dma: DMAARM |= DMA_ARM_CH3; // 45 nops to make sure the dma config is loaded hal_delay_45nop(); // send the very first UART byte to trigger a UART TX session: #if (SBUS_UART == USART0_P1) || (SBUS_UART == USART0_P0) U0DBUF = data[0]; #else U1DBUF = data[0]; #endif // SBUS_UART } #ifdef HUB_TELEMETRY_ON_SBUS_UART void HAL_UART_RX_ISR(void) { uint8_t rx; HAL_UART_RX_ISR_CLEAR_FLAG(); // THIS SHOULD NEVER BE THE LAST LINE IN AN ISR! #ifdef SBUS_INVERTED rx = 0xFF ^ HAL_UART_RX_GETCH(); // remove data inversion #else rx = HAL_UART_RX_GETCH(); #endif // SBUS_INVERTED if (uart_rx_callback != 0) { // execute callback uart_rx_callback(rx); } } #endif // HUB_TELEMETRY_ON_SBUS_UART