Repostiory containing DAPLink source code with Reset Pin workaround for HANI_IOT board.
Upstream: https://github.com/ARMmbed/DAPLink
Diff: source/hic_hal/atmel/sam3u2c/uart.c
- Revision:
- 0:01f31e923fe2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/source/hic_hal/atmel/sam3u2c/uart.c Tue Apr 07 12:55:42 2020 +0200 @@ -0,0 +1,452 @@ +/** + * @file uart.c + * @brief + * + * DAPLink Interface Firmware + * Copyright (c) 2009-2016, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "string.h" + +#include "sam3u.h" +#include "uart.h" +#include "circ_buf.h" +#include "cortex_m.h" +#include "util.h" +#include "settings.h" // for config_get_overflow_detect + +#define BUFFER_SIZE 512 +#define _CPU_CLK_HZ SystemCoreClock + +#define RX_OVRF_MSG "<DAPLink:Overflow>\n" +#define RX_OVRF_MSG_SIZE (sizeof(RX_OVRF_MSG) - 1) + + +#define I8 int8_t +#define I16 int16_t +#define I32 int32_t +#define U8 uint8_t +#define U16 uint16_t +#define U32 uint32_t + +#define BIT_CDC_USB2UART_CTS (9) +#define BIT_CDC_USB2UART_RTS (10) + +#define UART_PID (8) +#define UART_RX_PIN (11) +#define UART_TX_PIN (12) +#define UART_RXRDY_FLAG (1uL << 0) // Rx status flag +#define UART_TXRDY_FLAG (1uL << 1) // Tx RDY Status flag +#define UART_TXEMPTY_FLAG (1uL << 9) // Tx EMPTY Status flag +#define UART_ENDTX_FLAG (1uL << 4) // Tx end flag +#define UART_RX_ERR_FLAGS (0xE0) // Parity, framing, overrun error +#define UART_TX_INT_FLAG UART_TXEMPTY_FLAG +#define PIO_UART_PIN_MASK ((1uL << UART_RX_PIN) | (1uL << UART_TX_PIN)) + +#define PMC_BASE_ADDR (0x400E0400) +#define PMC_PCER *(volatile U32*)(PMC_BASE_ADDR + 0x10) // Peripheral clock enable register + +#define UART_BASE_ADDR (0x400E0600) +#define OFF_UART_CR (0x00) +#define OFF_UART_MR (0x04) +#define OFF_UART_IER (0x08) +#define OFF_UART_IDR (0x0C) +#define OFF_UART_IMR (0x10) +#define OFF_UART_SR (0x14) +#define OFF_UART_RHR (0x18) +#define OFF_UART_THR (0x1C) +#define OFF_UART_BRGR (0x20) +#define UART_CR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_CR) +#define UART_MR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_MR) +#define UART_IER *(volatile U32*)(UART_BASE_ADDR + OFF_UART_IER) +#define UART_IDR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_IDR) +#define UART_IMR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_IMR) +#define UART_SR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_SR) +#define UART_RHR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_RHR) +#define UART_THR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_THR) +#define UART_BRGR *(volatile U32*)(UART_BASE_ADDR + OFF_UART_BRGR) +#define OFF_PDC_RPR (0x100) +#define OFF_PDC_RCR (0x104) +#define OFF_PDC_TPR (0x108) +#define OFF_PDC_TCR (0x10C) +#define OFF_PDC_RNPR (0x110) +#define OFF_PDC_RNCR (0x114) +#define OFF_PDC_TNPR (0x118) +#define OFF_PDC_TNCR (0x11C) +#define OFF_PDC_PTCR (0x120) +#define OFF_PDC_PTSR (0x124) +#define UART_PDC_RPR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_RPR) +#define UART_PDC_RCR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_RCR) +#define UART_PDC_TPR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_TPR) +#define UART_PDC_TCR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_TCR) +#define UART_PDC_RNPR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_RNPR) +#define UART_PDC_RNCR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_RNCR) +#define UART_PDC_TNPR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_TNPR) +#define UART_PDC_TNCR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_TNCR) +#define UART_PDC_PTCR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_PTCR) +#define UART_PDC_PTSR *(volatile U32*)(UART_BASE_ADDR + OFF_PDC_PTSR) + +#define PIOA_BASE_ADDR (0x400E0C00) +#define PIOA_PDR (*(volatile U32*) (PIOA_BASE_ADDR + 0x04)) // PIO Disable Register +#define PIOA_IFER (*(volatile U32*) (PIOA_BASE_ADDR + 0x20)) // Input Filter Enable Register +#define PIOA_SODR (*(volatile U32*) (PIOA_BASE_ADDR + 0x30)) // Set output data +#define PIOA_CODR (*(volatile U32*) (PIOA_BASE_ADDR + 0x34)) // Clear output data register +#define PIOA_PDSR (*(volatile U32*) (PIOA_BASE_ADDR + 0x3c)) // pin data status register +#define PIOA_IER (*(volatile U32*) (PIOA_BASE_ADDR + 0x40)) // Interrupt Enable Register +#define PIOA_ISR (*(volatile U32*) (PIOA_BASE_ADDR + 0x4c)) // Interrupt Status Register +#define PIOA_ABSR (*(volatile U32*) (PIOA_BASE_ADDR + 0x70)) // Peripheral AB Select Register +#define PIOA_SCIFSR (*(volatile U32*) (PIOA_BASE_ADDR + 0x80)) // System Clock Glitch Input Filtering Select Register +#define PIOA_AIMER (*(volatile U32*) (PIOA_BASE_ADDR + 0xB0)) // Additional Interrupt Modes Enable Register +#define PIOA_ESR (*(volatile U32*) (PIOA_BASE_ADDR + 0xC0)) // Edge Select Register +#define PIOA_FELLSR (*(volatile U32*) (PIOA_BASE_ADDR + 0xD0)) // Falling Edge/Low Level Select Register +#define PIOA_REHLSR (*(volatile U32*) (PIOA_BASE_ADDR + 0xD4)) // Rising Edge/High Level Select Register + +circ_buf_t write_buffer; +uint8_t write_buffer_data[BUFFER_SIZE]; +circ_buf_t read_buffer; +uint8_t read_buffer_data[BUFFER_SIZE]; + +static U32 _Baudrate; +static U8 _FlowControl; +static U8 _UARTChar0; // Use static here since PDC starts transferring the byte when we already left this function +static U32 _TxInProgress; +static U8 _FlowControlEnabled = 1; + +static U32 _DetermineDivider(U32 Baudrate) +{ + U32 Div; + // + // Calculate divider for baudrate and round it correctly. + // This is necessary to get a tolerance as small as possible. + // + Div = Baudrate << 4; + Div = ((_CPU_CLK_HZ << 1) / Div) ;//+ 1; + Div = Div >> 1; + return Div; +} + +static int _SetBaudrate(U32 Baudrate) +{ + U32 Div; + Div = _DetermineDivider(Baudrate); + + if (Div >= 1) { + UART_BRGR = Div; + _Baudrate = _CPU_CLK_HZ / Div / 16; + return _Baudrate; + } + + return -1; +} + +static void _Send1(void) +{ + // Assert that there is data in the buffer + util_assert(circ_buf_count_used(&write_buffer) > 0); + + // + // Use PDC for transferring the byte to the UART since direct write to UART_THR does not seem to work properly. + // + PIOA->PIO_MDDR = (1 << UART_TX_PIN); //Disable open-drain on TX pin + _UARTChar0 = circ_buf_pop(&write_buffer); + + _TxInProgress = 1; + UART_PDC_TPR = (U32)&_UARTChar0; + UART_PDC_TCR = 1; + UART_PDC_PTCR = (1 << 8); // Enable transmission + UART_IER = UART_TX_INT_FLAG; // enable Tx interrupt +} + +static void _ResetBuffers(void) +{ + //TODO - assert that transmit is off + circ_buf_init(&write_buffer, write_buffer_data, sizeof(write_buffer_data)); + circ_buf_init(&read_buffer, read_buffer_data, sizeof(read_buffer_data)); + _TxInProgress = 0; +} + +static int get_tx_ready() +{ + if (!_FlowControlEnabled) { + return 1; + } + return ((PIOA->PIO_PDSR >> BIT_CDC_USB2UART_CTS) & 1) == 0; +} + +static void set_rx_ready(int ready) +{ + if (ready || !_FlowControlEnabled) { + PIOA->PIO_CODR = 1 << BIT_CDC_USB2UART_RTS; + } else { + PIOA->PIO_SODR = 1 << BIT_CDC_USB2UART_RTS; + } +} + +void UART_IntrEna(void) +{ + NVIC_EnableIRQ(UART_IRQn); // Enable USB interrupt +} + +void UART_IntrDis(void) +{ + NVIC_DisableIRQ(UART_IRQn); // Enable USB interrupt +} + +void uart_set_control_line_state(uint16_t ctrl_bmp) +{ +} + +void uart_software_flow_control() +{ + int v; + + if (((PIOA->PIO_PDSR >> BIT_CDC_USB2UART_CTS) & 1) == 0) { + _TxInProgress = 0; + v = circ_buf_count_used(&write_buffer); // NumBytes in write buffer + + if (v == 0) { // No more characters to send ?: Disable further tx interrupts + UART_IER = UART_TX_INT_FLAG; + } else { + _Send1(); //More bytes to send? Trigger sending of next byte + } + + } else { + UART_IDR = UART_TX_INT_FLAG; + } +} + +int32_t uart_initialize(void) +{ + // + // Initially, disable UART interrupt + // + UART_IntrDis(); + PMC->PMC_WPMR = 0x504D4300; // Disable write protect + PMC->PMC_PCER0 = (1 << UART_PID) | (1 << 10); // Enable peripheral clock for UART + PIOA + PMC->PMC_WPMR = 0x504D4301; // Enable write protect + PIOA_PDR = PIO_UART_PIN_MASK; // Enable peripheral output signals (disable PIO Port A) + PIOA_ABSR &= ~PIO_UART_PIN_MASK; // Select "A" peripherals on PIO A (UART Rx, Tx) + PIOA->PIO_MDER = PIO_UART_PIN_MASK; //Enable Multi Drive Control (Open Drain) on the UART Lines so that they don't power nRF51 + UART_CR = (0) + | (1 << 2) // RSTRX: Reset Receiver: 1 = The receiver logic is reset. + | (1 << 3) // RSTTX: Reset Transmitter: 1 = The transmitter logic is reset. + ; + UART_CR = (0) + | (0 << 2) // RSTRX: Release Receiver reset + | (0 << 3) // RSTTX: Release Transmitter reset + | (1 << 4) // RXEN: Receiver Enable + | (0 << 5) // RXDIS: Do not disable receiver + | (1 << 6) // TXEN: Transmitter Enable + | (0 << 7) // TXDIS: Do not disable transmitter + | (1 << 8) // RSTSTA: Reset status/error bits + ; + UART_MR = (0) + | (4 << 9) // PAR: Parity Type: 4 => No parity + | (0 << 14) // CHMODE: Channel Mode: 0 => Normal mode + ; + _SetBaudrate(9600); + _FlowControl = UART_FLOW_CONTROL_NONE; + UART_IDR = (0xFFFFFFFF); // Disable all interrupts + // + // Reset all status variables + // + _ResetBuffers(); + // + // Enable UART Tx/Rx interrupts + // + UART_IER = (0) + | (1 << 0) // Enable Rx Interrupt + | (0 << 9) // Initially disable TxEmpty Interrupt + | (0 << 4) // Initially disable ENDTx Interrupt + ; + // + //Set "RTS" to LOW to indicate that we are ready to receive + // + PIOA_CODR = (1uL << BIT_CDC_USB2UART_RTS); // RTS low: Ready to receive data + PIOA->PIO_OER = (1uL << BIT_CDC_USB2UART_RTS); // Pins == output + PIOA->PIO_PER = (1uL << BIT_CDC_USB2UART_RTS); // Pins == GPIO control + //Set CTS as input + PIOA->PIO_PER = (1uL << BIT_CDC_USB2UART_CTS); // Pins == GPIO control + PIOA->PIO_ODR = (1uL << BIT_CDC_USB2UART_CTS); // Pins == Input + PIOA->PIO_IER = (1uL << BIT_CDC_USB2UART_CTS); + // + // Finally, re-enable UART interrupt + // + //NVIC_SetPriority(UART_IRQn, 1); + UART_IntrEna(); + return 1; // O.K. ??? +} + +int32_t uart_uninitialize(void) +{ + UART_IntrDis(); + UART_IDR = (0xFFFFFFFF); // Disable all interrupts + _ResetBuffers(); + return 1; +} + +int32_t uart_reset(void) +{ + uart_initialize(); + return 1; +} + +int32_t uart_set_configuration(UART_Configuration *config) +{ + // + // UART always works with no parity, 1-stop bit + // Parity bit is configurable but not used in current implementation + // + UART_IntrDis(); + UART_IDR = (0xFFFFFFFF); // Disable all interrupts + UART_CR = (0) + | (1 << 5) // RXDIS: Disable receiver + | (1 << 7) // TXDIS: Disable transmitter + | (1 << 8) // RSTSTA: Reset status/error bits + ; + _FlowControl = config->FlowControl; + _SetBaudrate(config->Baudrate); + UART_CR = (0) + | (0 << 2) // RSTRX: Release Receiver reset + | (0 << 3) // RSTTX: Release Transmitter reset + | (1 << 4) // RXEN: Receiver Enable + | (0 << 5) // RXDIS: Do not disable receiver + | (1 << 6) // TXEN: Transmitter Enable + | (0 << 7) // TXDIS: Do not disable transmitter + | (1 << 8) // RSTSTA: Reset status/error bits + ; + UART_IER = (0) + | (1 << 0) // Enable Rx Interrupt + | (0 << 9) // Initially disable TxEmpty Interrupt + | (0 << 4) // Initially disable ENDTx Interrupt + ; + _ResetBuffers(); + UART_IntrEna(); + return 1; +} + + +int32_t uart_get_configuration(UART_Configuration *config) +{ + config->Baudrate = _Baudrate; + config->DataBits = UART_DATA_BITS_8; + config->FlowControl = (UART_FlowControl) _FlowControl;//UART_FLOW_CONTROL_NONE; + config->Parity = UART_PARITY_NONE; + config->StopBits = UART_STOP_BITS_1; + return 1; +} + +int32_t uart_write_free(void) +{ + return circ_buf_count_free(&write_buffer); +} + + +int32_t uart_write_data(uint8_t *data, uint16_t size) +{ + cortex_int_state_t state; + uint32_t cnt; + + cnt = circ_buf_write(&write_buffer, data, size); + + // + // Atomically trigger transfer if not already in progress + // + state = cortex_int_get_and_disable(); + if (_TxInProgress == 0 && get_tx_ready()) { + _Send1(); + } + cortex_int_restore(state); + + return cnt; +} + +int32_t uart_read_data(uint8_t *data, uint16_t size) +{ + cortex_int_state_t state; + uint32_t cnt; + + cnt = circ_buf_read(&read_buffer, data, size); + + // Atomically check if RTS had been asserted, if there is space on the buffer then deassert RTS + state = cortex_int_get_and_disable(); + if (circ_buf_count_free(&read_buffer) > RX_OVRF_MSG_SIZE) { + set_rx_ready(1); + } + cortex_int_restore(state); + + return cnt; +} + +void uart_enable_flow_control(bool enabled) +{ + _FlowControlEnabled = (U8)enabled; +} + +void UART_IRQHandler(void) +{ + int Status; + int32_t cnt; + U8 data; + Status = UART_SR; // Examine status register + + if (Status & UART_RX_ERR_FLAGS) { // In case of error: Set RSTSTA to reset status bits PARE, FRAME, OVRE and RXBRK + UART_CR = (1 << 8); + } + + // + // Handle Rx event + // + if (Status & UART_RXRDY_FLAG) { // Data received? + data = UART_RHR; + cnt = (int32_t)circ_buf_count_free(&read_buffer) - RX_OVRF_MSG_SIZE; + if (cnt > 0) { + circ_buf_push(&read_buffer, data); + } else if (config_get_overflow_detect()) { + if (0 == cnt) { + circ_buf_write(&read_buffer, (uint8_t*)RX_OVRF_MSG, RX_OVRF_MSG_SIZE); + } else { + // Drop newest + } + } else { + // Drop oldest + circ_buf_pop(&read_buffer); + circ_buf_push(&read_buffer, data); + } + + //If this was the last available byte on the buffer then assert RTS + if (cnt == 1) { + set_rx_ready(0); + } + } + + // + // Handle Tx event + // + if (Status & UART_IMR & UART_TX_INT_FLAG) { // Byte has been send by UART + cnt = circ_buf_count_used(&write_buffer); // NumBytes in write buffer + if (cnt == 0) { // No more characters to send ?: Disable further tx interrupts + UART_IDR = UART_TX_INT_FLAG; + PIOA->PIO_MDER = (1 << UART_TX_PIN); //enable open-drain + _TxInProgress = 0; + } else if (get_tx_ready()) { + _Send1(); //More bytes to send? Trigger sending of next byte + } else { + UART_IDR = UART_TX_INT_FLAG; // disable Tx interrupt + PIOA->PIO_MDER = (1 << UART_TX_PIN); //enable open-drain + } + } +}