Browse Source

Add Serial6 - T3.4 Uart5, T3.5 LPUart0

This is a WIP,  But I have now been able to create Serial6 on T3.4 beta,
and so far have tested Send/Receive basic stuff at 115200 and have tried
at several CPU speeds in MHZ (192, 216, 180, 120, 96)
teensy4-core
Kurt Eckhardt 8 years ago
parent
commit
7352ea7791
5 changed files with 928 additions and 0 deletions
  1. +7
    -0
      teensy3/HardwareSerial.h
  2. +10
    -0
      teensy3/HardwareSerial6.cpp
  3. +111
    -0
      teensy3/kinetis.h
  4. +362
    -0
      teensy3/serial6.c
  5. +438
    -0
      teensy3/serial6_lpuart.c

+ 7
- 0
teensy3/HardwareSerial.h View File

@@ -402,10 +402,17 @@ extern void serialEvent5(void);
class HardwareSerial6 : public HardwareSerial
{
public:
#if defined(__MK66FX1M0__) // For LPUART just pass baud straight in.
virtual void begin(uint32_t baud) { serial6_begin(baud); }
virtual void begin(uint32_t baud, uint32_t format) {
serial6_begin(baud);
serial6_format(format); }
#else
virtual void begin(uint32_t baud) { serial6_begin(BAUD2DIV3(baud)); }
virtual void begin(uint32_t baud, uint32_t format) {
serial6_begin(BAUD2DIV3(baud));
serial6_format(format); }
#endif
virtual void end(void) { serial6_end(); }
virtual void transmitterEnable(uint8_t pin) { serial6_set_transmit_pin(pin); }
virtual void setRX(uint8_t pin) { serial6_set_rx(pin); }

+ 10
- 0
teensy3/HardwareSerial6.cpp View File

@@ -0,0 +1,10 @@
#include "HardwareSerial.h"

#if defined(HAS_KINETISK_UART5) || defined (HAS_KINETISK_LPUART0)

HardwareSerial6 Serial6;

void serialEvent6() __attribute__((weak));
void serialEvent6() {}

#endif

+ 111
- 0
teensy3/kinetis.h View File

@@ -5014,6 +5014,39 @@ typedef struct __attribute__((packed)) {
#define UART4_ET7816 (KINETISK_UART4.ET7816) // UART 7816 Error Threshold Register
#define UART4_TL7816 (KINETISK_UART4.TL7816) // UART 7816 Transmit Length Register

#define KINETISK_UART5 (*(KINETISK_UART_t *)0x400EB000)
#define UART5_BDH (KINETISK_UART5.BDH) // UART Baud Rate Registers: High
#define UART5_BDL (KINETISK_UART5.BDL) // UART Baud Rate Registers: Low
#define UART5_C1 (KINETISK_UART5.C1) // UART Control Register 1
#define UART5_C2 (KINETISK_UART5.C2) // UART Control Register 2
#define UART5_S1 (KINETISK_UART5.S1) // UART Status Register 1
#define UART5_S2 (KINETISK_UART5.S2) // UART Status Register 2
#define UART5_C3 (KINETISK_UART5.C3) // UART Control Register 3
#define UART5_D (KINETISK_UART5.D) // UART Data Register
#define UART5_MA1 (KINETISK_UART5.MA1) // UART Match Address Registers 1
#define UART5_MA2 (KINETISK_UART5.MA2) // UART Match Address Registers 2
#define UART5_C4 (KINETISK_UART5.C4) // UART Control Register 4
#define UART5_C5 (KINETISK_UART5.C5) // UART Control Register 5
#define UART5_ED (KINETISK_UART5.ED) // UART Extended Data Register
#define UART5_MODEM (KINETISK_UART5.MODEM) // UART Modem Register
#define UART5_IR (KINETISK_UART5.IR) // UART Infrared Register
#define UART5_PFIFO (KINETISK_UART5.PFIFO) // UART FIFO Parameters
#define UART5_CFIFO (KINETISK_UART5.CFIFO) // UART FIFO Control Register
#define UART5_SFIFO (KINETISK_UART5.SFIFO) // UART FIFO Status Register
#define UART5_TWFIFO (KINETISK_UART5.TWFIFO) // UART FIFO Transmit Watermark
#define UART5_TCFIFO (KINETISK_UART5.TCFIFO) // UART FIFO Transmit Count
#define UART5_RWFIFO (KINETISK_UART5.RWFIFO) // UART FIFO Receive Watermark
#define UART5_RCFIFO (KINETISK_UART5.RCFIFO) // UART FIFO Receive Count
#define UART5_C7816 (KINETISK_UART5.C7816) // UART 7816 Control Register
#define UART5_IE7816 (KINETISK_UART5.IE7816) // UART 7816 Interrupt Enable Register
#define UART5_IS7816 (KINETISK_UART5.IS7816) // UART 7816 Interrupt Status Register
#define UART5_WP7816T0 (KINETISK_UART5.WP7816T0)// UART 7816 Wait Parameter Register
#define UART5_WP7816T1 (KINETISK_UART5.WP7816T1)// UART 7816 Wait Parameter Register
#define UART5_WN7816 (KINETISK_UART5.WN7816) // UART 7816 Wait N Register
#define UART5_WF7816 (KINETISK_UART5.WF7816) // UART 7816 Wait FD Register
#define UART5_ET7816 (KINETISK_UART5.ET7816) // UART 7816 Error Threshold Register
#define UART5_TL7816 (KINETISK_UART5.TL7816) // UART 7816 Transmit Length Register


// Secured digital host controller (SDHC)

@@ -5185,6 +5218,84 @@ typedef struct __attribute__((packed)) {
#define SDHC_MMCBOOT_DTOCVACK(n) (uint32_t)(((n) & 0xF)<<0) // Boot ACK Time Out Counter Value
#define SDHC_HOSTVER (*(volatile uint32_t *)0x400B10FC) // Host Controller Version

///////////////////////////////////
// Low Power Asynchronous Receiver/Transmitter (LPUART)

typedef struct __attribute__((packed)) {
volatile uint32_t BAUD;
volatile uint32_t STAT;
volatile uint32_t CTRL;
volatile uint32_t DATA;
volatile uint32_t MATCH;
volatile uint32_t MODIR;
} KINETISK_LPUART_t;
#define KINETISK_LPUART0 (*(KINETISK_LPUART_t *)0x400C4000)
#define LPUART0_BAUD (KINETISK_LPUART0.BAUD) // LPUART Baud Register
#define LPUART_BAUD_MAEN1 ((uint32_t)0x80000000) // Enable automatic address or data maching
#define LPUART_BAUD_MAEN2 ((uint32_t)0x40000000) // Enable automatic address or data maching
#define LPUART_BAUD_M10 ((uint32_t)0x20000000) // 10-bit Mode select
#define LPUART_BAUD_OSR(n) ((uint32_t)((n) & 0x1f) << 24) // Over sampling ratio
#define LPUART_BAUD_TDMAE ((uint32_t)0x00800000) // Transmitter Dma Enable
#define LPUART_BAUD_RDMAE ((uint32_t)0x00400000) // Receiver Dma Enable
#define LPUART_BAUD_BOTHEDGE ((uint32_t)0x00020000) // Both edge sampling needed OSR 4-7
#define LPUART_BAUD_SBR(n) ((uint32_t)((n) & 0x1fff) << 0) // set baud rate divisor

#define LPUART0_STAT (KINETISK_LPUART0.STAT) // LPUART Status register
#define LPUART_STAT_LBKDIF ((uint32_t)0x80000000) // LIN Break Detect Interrupt Flag
#define LPUART_STAT_RXEDGIF ((uint32_t)0x40000000) // RxD Pin Active Edge Interrupt Flag
#define LPUART_STAT_MSBF ((uint32_t)0x20000000) // Most Significant Bit First
#define LPUART_STAT_RXINV ((uint32_t)0x10000000) // Receive Data Inversion
#define LPUART_STAT_RWUID ((uint32_t)0x08000000) // Receive Wakeup Idle Detect
#define LPUART_STAT_BRK13 ((uint32_t)0x04000000) // Break Transmit Character Length
#define LPUART_STAT_LBKDE ((uint32_t)0x02000000) // LIN Break Detection Enable
#define LPUART_STAT_RAF ((uint32_t)0x01000000) // Receiver Active Flag
#define LPUART_STAT_TDRE ((uint32_t)0x00800000) // Transmit Data Register Empty Flag
#define LPUART_STAT_TC ((uint32_t)0x00400000) // Transmit Complete Flag
#define LPUART_STAT_RDRF ((uint32_t)0x00200000) // Receive Data Register Full Flag
#define LPUART_STAT_IDLE ((uint32_t)0x00100000) // Idle Line Flag
#define LPUART_STAT_OR ((uint32_t)0x00080000) // Receiver Overrun Flag
#define LPUART_STAT_NF ((uint32_t)0x00040000) // Noise Flag
#define LPUART_STAT_FE ((uint32_t)0x00020000) // Framing Error Flag
#define LPUART_STAT_PF ((uint32_t)0x00010000) // Parity Error Flag
#define LPUART_STAT_MA1F ((uint32_t)0x00008000) // Match 1 Flag
#define LPUART_STAT_MA2F ((uint32_t)0x00004000) // Match 2 Flag
#define LPUART0_CTRL (KINETISK_LPUART0.CTRL) // LPUART Control register
#define LPUART_CTRL_R8 ((uint32_t)0x80000000) // Received Bit 8
#define LPUART_CTRL_T8 ((uint32_t)0x40000000) // Transmit Bit 8
#define LPUART_CTRL_TXDIR ((uint32_t)0x20000000) // TX Pin Direction in Single-Wire mode
#define LPUART_CTRL_TXINV ((uint32_t)0x10000000) // Transmit Data Inversion
#define LPUART_CTRL_ORIE ((uint32_t)0x08000000) // Overrun Error Interrupt Enable
#define LPUART_CTRL_NEIE ((uint32_t)0x04000000) // Noise Error Interrupt Enable
#define LPUART_CTRL_FEIE ((uint32_t)0x02000000) // Framing Error Interrupt Enable
#define LPUART_CTRL_PEIE ((uint32_t)0x01000000) // Parity Error Interrupt Enable
#define LPUART_CTRL_TIE ((uint32_t)0x00800000) // Transmitter Interrupt or DMA Transfer Enable.
#define LPUART_CTRL_TCIE ((uint32_t)0x00400000) // Transmission Complete Interrupt Enable
#define LPUART_CTRL_RIE ((uint32_t)0x00200000) // Receiver Full Interrupt or DMA Transfer Enable
#define LPUART_CTRL_ILIE ((uint32_t)0x00100000) // Idle Line Interrupt Enable
#define LPUART_CTRL_TE ((uint32_t)0x00080000) // Transmitter Enable
#define LPUART_CTRL_RE ((uint32_t)0x00040000) // Receiver Enable
#define LPUART_CTRL_RWU ((uint32_t)0x00020000) // Receiver Wakeup Control
#define LPUART_CTRL_SBK ((uint32_t)0x00010000) // Send Break
#define LPUART_CTRL_MAEN1 ((uint32_t)0x00008000) // Match Address Mode Enable 1
#define LPUART_CTRL_MAEN2 ((uint32_t)0x00004000) // Match Address Mode Enable 2
#define LPUART_CTRL_LOOPS ((uint32_t)0x00000080) // When LOOPS is set, the RxD pin is disconnected from the UART and the transmitter output is internally connected to the receiver input
#define LPUART_CTRL_UARTSWAI ((uint32_t)0x00000040) // UART Stops in Wait Mode
#define LPUART_CTRL_RSRC ((uint32_t)0x00000020) // When LOOPS is set, the RSRC field determines the source for the receiver shift register input
#define LPUART_CTRL_M ((uint32_t)0x00000010) // 9-bit or 8-bit Mode Select
#define LPUART_CTRL_WAKE ((uint32_t)0x00000008) // Determines which condition wakes the UART
#define LPUART_CTRL_ILT ((uint32_t)0x00000004) // Idle Line Type Select
#define LPUART_CTRL_PE ((uint32_t)0x00000002) // Parity Enable
#define LPUART_CTRL_PT ((uint32_t)0x00000001) // Parity Type, 0=even, 1=odd

#define LPUART0_DATA (KINETISK_LPUART0.DATA) // LPUART Data register
#define LPUART_DATA_NOISY ((uint32_t)0x00080000) // Data received with noise
#define LPUART_DATA_PARITY ((uint32_t)0x00040000) // Data received with Parity error
#define LPUART_DATA_FRETSC ((uint32_t)0x00020000) // Frame error/Transmit Special char
#define LPUART_DATA_RXEMPT ((uint32_t)0x00010000) // receive buffer empty
#define LPUART_DATA_IDLINE ((uint32_t)0x00008000) // Match Address Mode Enable 1
#define LPUART0_MATCH (KINETISK_LPUART0.MATCH) // LPUART Match register
#define LPUART0_MODIR (KINETISK_LPUART0.MODIR) // LPUART Modem IrDA Register


// Synchronous Audio Interface (SAI)


+ 362
- 0
teensy3/serial6.c View File

@@ -0,0 +1,362 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2013 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include "kinetis.h"
#include "core_pins.h"
#include "HardwareSerial.h"

#ifdef HAS_KINETISK_UART5

////////////////////////////////////////////////////////////////
// Tunable parameters (relatively safe to edit these numbers)
////////////////////////////////////////////////////////////////

#define TX_BUFFER_SIZE 40 // number of outgoing bytes to buffer
#define RX_BUFFER_SIZE 64 // number of incoming bytes to buffer
#define RTS_HIGH_WATERMARK 40 // RTS requests sender to pause
#define RTS_LOW_WATERMARK 26 // RTS allows sender to resume
#define IRQ_PRIORITY 64 // 0 = highest priority, 255 = lowest


////////////////////////////////////////////////////////////////
// changes not recommended below this point....
////////////////////////////////////////////////////////////////

#ifdef SERIAL_9BIT_SUPPORT
static uint8_t use9Bits = 0;
#define BUFTYPE uint16_t
#else
#define BUFTYPE uint8_t
#define use9Bits 0
#endif

static volatile BUFTYPE tx_buffer[TX_BUFFER_SIZE];
static volatile BUFTYPE rx_buffer[RX_BUFFER_SIZE];
static volatile uint8_t transmitting = 0;
static volatile uint8_t *transmit_pin=NULL;
#define transmit_assert() *transmit_pin = 1
#define transmit_deassert() *transmit_pin = 0
static volatile uint8_t *rts_pin=NULL;
#define rts_assert() *rts_pin = 0
#define rts_deassert() *rts_pin = 1
#if TX_BUFFER_SIZE > 255
static volatile uint16_t tx_buffer_head = 0;
static volatile uint16_t tx_buffer_tail = 0;
#else
static volatile uint8_t tx_buffer_head = 0;
static volatile uint8_t tx_buffer_tail = 0;
#endif
#if RX_BUFFER_SIZE > 255
static volatile uint16_t rx_buffer_head = 0;
static volatile uint16_t rx_buffer_tail = 0;
#else
static volatile uint8_t rx_buffer_head = 0;
static volatile uint8_t rx_buffer_tail = 0;
#endif

static uint8_t tx_pin_num = 34;

// UART0 and UART1 are clocked by F_CPU, UART2 is clocked by F_BUS
// UART0 has 8 byte fifo, UART1 and UART2 have 1 byte buffer

#define C2_ENABLE UART_C2_TE | UART_C2_RE | UART_C2_RIE
#define C2_TX_ACTIVE C2_ENABLE | UART_C2_TIE
#define C2_TX_COMPLETING C2_ENABLE | UART_C2_TCIE
#define C2_TX_INACTIVE C2_ENABLE

void serial6_begin(uint32_t divisor)
{
SIM_SCGC1 |= SIM_SCGC1_UART4; // turn on clock, TODO: use bitband
rx_buffer_head = 0;
rx_buffer_tail = 0;
tx_buffer_head = 0;
tx_buffer_tail = 0;
transmitting = 0;
CORE_PIN47_CONFIG = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_PFE | PORT_PCR_MUX(3);
CORE_PIN48_CONFIG = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);
UART5_BDH = (divisor >> 13) & 0x1F;
UART5_BDL = (divisor >> 5) & 0xFF;
UART5_C4 = divisor & 0x1F;
UART5_C1 = 0;
UART5_PFIFO = 0;
UART5_C2 = C2_TX_INACTIVE;
NVIC_SET_PRIORITY(IRQ_UART5_STATUS, IRQ_PRIORITY);
NVIC_ENABLE_IRQ(IRQ_UART5_STATUS);
}

void serial6_format(uint32_t format)
{
uint8_t c;

c = UART5_C1;
c = (c & ~0x13) | (format & 0x03); // configure parity
if (format & 0x04) c |= 0x10; // 9 bits (might include parity)
UART5_C1 = c;
if ((format & 0x0F) == 0x04) UART5_C3 |= 0x40; // 8N2 is 9 bit with 9th bit always 1
c = UART5_S2 & ~0x10;
if (format & 0x10) c |= 0x10; // rx invert
UART5_S2 = c;
c = UART5_C3 & ~0x10;
if (format & 0x20) c |= 0x10; // tx invert
UART5_C3 = c;
#ifdef SERIAL_9BIT_SUPPORT
c = UART5_C4 & 0x1F;
if (format & 0x08) c |= 0x20; // 9 bit mode with parity (requires 10 bits)
UART5_C4 = c;
use9Bits = format & 0x80;
#endif
}

void serial6_end(void)
{
if (!(SIM_SCGC1 & SIM_SCGC1_UART4)) return;
while (transmitting) yield(); // wait for buffered data to send
NVIC_DISABLE_IRQ(IRQ_UART5_STATUS);
UART5_C2 = 0;
CORE_PIN47_CONFIG = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
CORE_PIN48_CONFIG = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
rx_buffer_head = 0;
rx_buffer_tail = 0;
if (rts_pin) rts_deassert();
}

void serial6_set_transmit_pin(uint8_t pin)
{
while (transmitting) ;
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
transmit_pin = portOutputRegister(pin);
}

void serial6_set_tx(uint8_t pin, uint8_t opendrain)
{
uint32_t cfg;

if (opendrain) pin |= 128;
if (pin == tx_pin_num) return;
if ((SIM_SCGC4 & SIM_SCGC4_UART2)) {
switch (tx_pin_num & 127) {
case 48: CORE_PIN48_CONFIG = 0; break; // PTE24
}
if (opendrain) {
cfg = PORT_PCR_DSE | PORT_PCR_ODE;
} else {
cfg = PORT_PCR_DSE | PORT_PCR_SRE;
}
switch (pin & 127) {
case 48: CORE_PIN48_CONFIG = cfg | PORT_PCR_MUX(3); break;
}
}
tx_pin_num = pin;
}

void serial6_set_rx(uint8_t pin)
{
}

int serial6_set_rts(uint8_t pin)
{
if (!(SIM_SCGC1 & SIM_SCGC1_UART4)) return 0;
if (pin < CORE_NUM_DIGITAL) {
rts_pin = portOutputRegister(pin);
pinMode(pin, OUTPUT);
rts_assert();
} else {
rts_pin = NULL;
return 0;
}
return 1;
}

int serial6_set_cts(uint8_t pin)
{
if (!(SIM_SCGC1 & SIM_SCGC1_UART4)) return 0;
if (pin == 56) {
CORE_PIN56_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_PE; // weak pulldown
} else {
UART5_MODEM &= ~UART_MODEM_TXCTSE;
return 0;
}
UART5_MODEM |= UART_MODEM_TXCTSE;
return 1;
}

void serial6_putchar(uint32_t c)
{
uint32_t head, n;

if (!(SIM_SCGC1 & SIM_SCGC1_UART4)) return;
if (transmit_pin) transmit_assert();
head = tx_buffer_head;
if (++head >= TX_BUFFER_SIZE) head = 0;
while (tx_buffer_tail == head) {
int priority = nvic_execution_priority();
if (priority <= IRQ_PRIORITY) {
if ((UART5_S1 & UART_S1_TDRE)) {
uint32_t tail = tx_buffer_tail;
if (++tail >= TX_BUFFER_SIZE) tail = 0;
n = tx_buffer[tail];
if (use9Bits) UART5_C3 = (UART5_C3 & ~0x40) | ((n & 0x100) >> 2);
UART5_D = n;
tx_buffer_tail = tail;
}
} else if (priority >= 256) {
yield(); // wait
}
}
tx_buffer[head] = c;
transmitting = 1;
tx_buffer_head = head;
UART5_C2 = C2_TX_ACTIVE;
}

void serial6_write(const void *buf, unsigned int count)
{
const uint8_t *p = (const uint8_t *)buf;
while (count-- > 0) serial6_putchar(*p++);
}

void serial6_flush(void)
{
while (transmitting) yield(); // wait
}

int serial6_write_buffer_free(void)
{
uint32_t head, tail;

head = tx_buffer_head;
tail = tx_buffer_tail;
if (head >= tail) return TX_BUFFER_SIZE - 1 - head + tail;
return tail - head - 1;
}

int serial6_available(void)
{
uint32_t head, tail;

head = rx_buffer_head;
tail = rx_buffer_tail;
if (head >= tail) return head - tail;
return RX_BUFFER_SIZE + head - tail;
}

int serial6_getchar(void)
{
uint32_t head, tail;
int c;

head = rx_buffer_head;
tail = rx_buffer_tail;
if (head == tail) return -1;
if (++tail >= RX_BUFFER_SIZE) tail = 0;
c = rx_buffer[tail];
rx_buffer_tail = tail;
if (rts_pin) {
int avail;
if (head >= tail) avail = head - tail;
else avail = RX_BUFFER_SIZE + head - tail;
if (avail <= RTS_LOW_WATERMARK) rts_assert();
}
return c;
}

int serial6_peek(void)
{
uint32_t head, tail;

head = rx_buffer_head;
tail = rx_buffer_tail;
if (head == tail) return -1;
if (++tail >= RX_BUFFER_SIZE) tail = 0;
return rx_buffer[tail];
}

void serial6_clear(void)
{
rx_buffer_head = rx_buffer_tail;
if (rts_pin) rts_assert();
}

// status interrupt combines
// Transmit data below watermark UART_S1_TDRE
// Transmit complete UART_S1_TC
// Idle line UART_S1_IDLE
// Receive data above watermark UART_S1_RDRF
// LIN break detect UART_S2_LBKDIF
// RxD pin active edge UART_S2_RXEDGIF

void UART5_status_isr(void)
{
uint32_t head, tail, n;
uint8_t c;

if (UART5_S1 & UART_S1_RDRF) {
if (use9Bits && (UART5_C3 & 0x80)) {
n = UART5_D | 0x100;
} else {
n = UART5_D;
}
head = rx_buffer_head + 1;
if (head >= RX_BUFFER_SIZE) head = 0;
if (head != rx_buffer_tail) {
rx_buffer[head] = n;
rx_buffer_head = head;
}
if (rts_pin) {
int avail;
tail = tx_buffer_tail;
if (head >= tail) avail = head - tail;
else avail = RX_BUFFER_SIZE + head - tail;
if (avail >= RTS_HIGH_WATERMARK) rts_deassert();
}
}
c = UART5_C2;
if ((c & UART_C2_TIE) && (UART5_S1 & UART_S1_TDRE)) {
head = tx_buffer_head;
tail = tx_buffer_tail;
if (head == tail) {
UART5_C2 = C2_TX_COMPLETING;
} else {
if (++tail >= TX_BUFFER_SIZE) tail = 0;
n = tx_buffer[tail];
if (use9Bits) UART5_C3 = (UART5_C3 & ~0x40) | ((n & 0x100) >> 2);
UART5_D = n;
tx_buffer_tail = tail;
}
}
if ((c & UART_C2_TCIE) && (UART5_S1 & UART_S1_TC)) {
transmitting = 0;
if (transmit_pin) transmit_deassert();
UART5_C2 = C2_TX_INACTIVE;
}
}

#endif // HAS_KINETISK_UART4

+ 438
- 0
teensy3/serial6_lpuart.c View File

@@ -0,0 +1,438 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2013 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#include "kinetis.h"
#include "core_pins.h"
#include "HardwareSerial.h"

#ifdef HAS_KINETISK_LPUART0


////////////////////////////////////////////////////////////////
// Tunable parameters (relatively safe to edit these numbers)
////////////////////////////////////////////////////////////////

#define TX_BUFFER_SIZE 40 // number of outgoing bytes to buffer
#define RX_BUFFER_SIZE 64 // number of incoming bytes to buffer
#define RTS_HIGH_WATERMARK 40 // RTS requests sender to pause
#define RTS_LOW_WATERMARK 26 // RTS allows sender to resume
#define IRQ_PRIORITY 64 // 0 = highest priority, 255 = lowest


////////////////////////////////////////////////////////////////
// changes not recommended below this point....
////////////////////////////////////////////////////////////////

#ifdef SERIAL_9BIT_SUPPORT
static uint8_t use9Bits = 0;
#define BUFTYPE uint16_t
#else
#define BUFTYPE uint8_t
#define use9Bits 0
#endif

static volatile BUFTYPE tx_buffer[TX_BUFFER_SIZE];
static volatile BUFTYPE rx_buffer[RX_BUFFER_SIZE];
static volatile uint8_t transmitting = 0;
static volatile uint8_t *transmit_pin=NULL;
#define transmit_assert() *transmit_pin = 1
#define transmit_deassert() *transmit_pin = 0
static volatile uint8_t *rts_pin=NULL;
#define rts_assert() *rts_pin = 0
#define rts_deassert() *rts_pin = 1
#if TX_BUFFER_SIZE > 255
static volatile uint16_t tx_buffer_head = 0;
static volatile uint16_t tx_buffer_tail = 0;
#else
static volatile uint8_t tx_buffer_head = 0;
static volatile uint8_t tx_buffer_tail = 0;
#endif
#if RX_BUFFER_SIZE > 255
static volatile uint16_t rx_buffer_head = 0;
static volatile uint16_t rx_buffer_tail = 0;
#else
static volatile uint8_t rx_buffer_head = 0;
static volatile uint8_t rx_buffer_tail = 0;
#endif

static uint8_t tx_pin_num = 34;

// UART0 and UART1 are clocked by F_CPU, UART2 is clocked by F_BUS
// UART0 has 8 byte fifo, UART1 and UART2 have 1 byte buffer


void serial6_begin(uint32_t desiredBaudRate)
{
#define F_LPUART_CLOCK_SPEED 48000000 //F_BUS
// Make sure the clock for this uart is enabled, else the registers are not
// vailable.
SIM_SCGC2 |= SIM_SCGC2_LPUART0; // Turn on the clock

// Convert the baud rate to best divisor and OSR, based off of code I found in posting
// try to find an OSR > 4 with the minimum difference from the actual disired baud rate.
uint16_t sbr, sbrTemp, osrCheck;
uint32_t osr, baudDiffCheck, calculatedBaud, baudDiff;
uint32_t clockSpeed;

// First lets figure out what the LPUART Clock speed is.
uint32_t PLLFLLSEL = SIM_SOPT2 & SIM_SOPT2_IRC48SEL; // Note: Bot bits on here

if (PLLFLLSEL == SIM_SOPT2_IRC48SEL)
clockSpeed = 48000000; // Fixed to 48mhz
else if (PLLFLLSEL == SIM_SOPT2_PLLFLLSEL)
clockSpeed = F_PLL; // Using PLL clock
else
clockSpeed = F_CPU/4; // FLL clock, guessing
osr = 4;
sbr = (clockSpeed/(desiredBaudRate * osr));
/*set sbr to 1 if the clockSpeed can not satisfy the desired baud rate*/
if(sbr == 0) {
// Maybe print something.
return; // can not initialize
}

// With integer math the divide*muliply implies the calculated baud will be >= desired baud
calculatedBaud = (clockSpeed / (osr * sbr));
baudDiff = calculatedBaud - desiredBaudRate;

// Check if better off with sbr+1
if (baudDiff != 0) {
calculatedBaud = (clockSpeed / (osr * (sbr + 1)));
baudDiffCheck = desiredBaudRate - calculatedBaud ;
if (baudDiffCheck < baudDiff) {
sbr++; // use the higher sbr
baudDiff = baudDiffCheck;
}
}

// loop to find the best osr value possible, one that generates minimum baudDiff
for (osrCheck = 5; osrCheck <= 32; osrCheck++) {
sbrTemp = (clockSpeed/(desiredBaudRate * osrCheck));

if(sbrTemp == 0)
break; // higher divisor returns 0 so can not use...

// Remember integer math so (X/Y)*Y will always be <=X
calculatedBaud = (clockSpeed / (osrCheck * sbrTemp));
baudDiffCheck = calculatedBaud - desiredBaudRate;
if (baudDiffCheck <= baudDiff) {
baudDiff = baudDiffCheck;
osr = osrCheck;
sbr = sbrTemp;
}
// Lets try the rounded up one as well
if (baudDiffCheck) {
calculatedBaud = (clockSpeed / (osrCheck * ++sbrTemp));
baudDiffCheck = desiredBaudRate - calculatedBaud;
if (baudDiffCheck <= baudDiff) {
baudDiff = baudDiffCheck;
osr = osrCheck;
sbr = sbrTemp;
}
}
}
// for lower OSR <= 7x turn on both edge sampling
uint32_t lpb = LPUART_BAUD_OSR(osr-1) | LPUART_BAUD_SBR(sbr);
if (osr < 8) {
lpb |= LPUART_BAUD_BOTHEDGE;
}
LPUART0_BAUD = lpb;

SIM_SOPT2 |= SIM_SOPT2_LPUARTSRC(1); // Lets use PLL?

rx_buffer_head = 0;
rx_buffer_tail = 0;
tx_buffer_head = 0;
tx_buffer_tail = 0;
transmitting = 0;
CORE_PIN47_CONFIG = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_PFE | PORT_PCR_MUX(5);
CORE_PIN48_CONFIG = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(5);
LPUART0_CTRL = 0;
LPUART0_MATCH = 0;
LPUART0_STAT = 0;

// Enable the transmitter, receiver and enable receiver interrupt
LPUART0_CTRL |= LPUART_CTRL_RIE | LPUART_CTRL_TE | LPUART_CTRL_RE;
NVIC_SET_PRIORITY(IRQ_LPUART0, IRQ_PRIORITY);
NVIC_ENABLE_IRQ(IRQ_LPUART0);
}

void serial6_format(uint32_t format)
{
uint32_t c;

c = LPUART0_CTRL;
c = (c & ~0x13) | (format & 0x03); // configure parity
if (format & 0x04) c |= 0x10; // 9 bits (might include parity)
LPUART0_CTRL = c;
if ((format & 0x0F) == 0x04) LPUART0_CTRL |= LPUART_CTRL_T8; // 8N2 is 9 bit with 9th bit always 1
// c = UART5_S2 & ~0x10;
// if (format & 0x10) c |= 0x10; // rx invert
// UART5_S2 = c;
c = LPUART0_CTRL & ~LPUART_CTRL_TXINV;
if (format & 0x20) c |= LPUART_CTRL_TXINV; // tx invert
LPUART0_CTRL = c;
#if 0
c = UART5_C4 & 0x1F;
if (format & 0x08) c |= 0x20; // 9 bit mode with parity (requires 10 bits)
UART5_C4 = c;
use9Bits = format & 0x80;
#endif
}

void serial6_end(void)
{
if (!(SIM_SCGC2 & SIM_SCGC2_LPUART0)) return;
while (transmitting) yield(); // wait for buffered data to send
NVIC_DISABLE_IRQ(IRQ_LPUART0);
LPUART0_CTRL = 0;
CORE_PIN47_CONFIG = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
CORE_PIN48_CONFIG = PORT_PCR_PE | PORT_PCR_PS | PORT_PCR_MUX(1);
rx_buffer_head = 0;
rx_buffer_tail = 0;
if (rts_pin) rts_deassert();
}

void serial6_set_transmit_pin(uint8_t pin)
{
while (transmitting) ;
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
transmit_pin = portOutputRegister(pin);
}

void serial6_set_tx(uint8_t pin, uint8_t opendrain)
{
uint32_t cfg;

if (opendrain) pin |= 128;
if (pin == tx_pin_num) return;
if ((SIM_SCGC4 & SIM_SCGC4_UART2)) {
switch (tx_pin_num & 127) {
case 48: CORE_PIN48_CONFIG = 0; break; // PTE24
}
if (opendrain) {
cfg = PORT_PCR_DSE | PORT_PCR_ODE;
} else {
cfg = PORT_PCR_DSE | PORT_PCR_SRE;
}
switch (pin & 127) {
case 48: CORE_PIN48_CONFIG = cfg | PORT_PCR_MUX(3); break;
}
}
tx_pin_num = pin;
}

void serial6_set_rx(uint8_t pin)
{
}

int serial6_set_rts(uint8_t pin)
{
if (!(SIM_SCGC2 & SIM_SCGC2_LPUART0)) return 0;
if (pin < CORE_NUM_DIGITAL) {
rts_pin = portOutputRegister(pin);
pinMode(pin, OUTPUT);
rts_assert();
} else {
rts_pin = NULL;
return 0;
}
return 1;
}

int serial6_set_cts(uint8_t pin)
{
if (!(SIM_SCGC2 & SIM_SCGC2_LPUART0)) return 0;
if (pin == 56) {
CORE_PIN56_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_PE; // weak pulldown
} else {
UART5_MODEM &= ~UART_MODEM_TXCTSE;
return 0;
}
UART5_MODEM |= UART_MODEM_TXCTSE;
return 1;
}

void serial6_putchar(uint32_t c)
{
uint32_t head, n;

if (!(SIM_SCGC2 & SIM_SCGC2_LPUART0)) return;
if (transmit_pin) transmit_assert();
head = tx_buffer_head;
if (++head >= TX_BUFFER_SIZE) head = 0;
while (tx_buffer_tail == head) {
int priority = nvic_execution_priority();
if (priority <= IRQ_PRIORITY) {
if ((LPUART0_STAT & LPUART_STAT_TDRE)) {
uint32_t tail = tx_buffer_tail;
if (++tail >= TX_BUFFER_SIZE) tail = 0;
n = tx_buffer[tail];
//if (use9Bits) UART5_C3 = (UART5_C3 & ~0x40) | ((n & 0x100) >> 2);
LPUART0_DATA = n;
tx_buffer_tail = tail;
}
} else if (priority >= 256) {
yield(); // wait
}
}
tx_buffer[head] = c;
transmitting = 1;
tx_buffer_head = head;
LPUART0_CTRL |= LPUART_CTRL_TIE; // enable the transmit interrupt
}

void serial6_write(const void *buf, unsigned int count)
{
const uint8_t *p = (const uint8_t *)buf;
while (count-- > 0) serial6_putchar(*p++);
}

void serial6_flush(void)
{
while (transmitting) yield(); // wait
}

int serial6_write_buffer_free(void)
{
uint32_t head, tail;

head = tx_buffer_head;
tail = tx_buffer_tail;
if (head >= tail) return TX_BUFFER_SIZE - 1 - head + tail;
return tail - head - 1;
}

int serial6_available(void)
{
uint32_t head, tail;

head = rx_buffer_head;
tail = rx_buffer_tail;
if (head >= tail) return head - tail;
return RX_BUFFER_SIZE + head - tail;
}

int serial6_getchar(void)
{
uint32_t head, tail;
int c;

head = rx_buffer_head;
tail = rx_buffer_tail;
if (head == tail) return -1;
if (++tail >= RX_BUFFER_SIZE) tail = 0;
c = rx_buffer[tail];
rx_buffer_tail = tail;
if (rts_pin) {
int avail;
if (head >= tail) avail = head - tail;
else avail = RX_BUFFER_SIZE + head - tail;
if (avail <= RTS_LOW_WATERMARK) rts_assert();
}
return c;
}

int serial6_peek(void)
{
uint32_t head, tail;

head = rx_buffer_head;
tail = rx_buffer_tail;
if (head == tail) return -1;
if (++tail >= RX_BUFFER_SIZE) tail = 0;
return rx_buffer[tail];
}

void serial6_clear(void)
{
rx_buffer_head = rx_buffer_tail;
if (rts_pin) rts_assert();
}

// status interrupt combines
// Transmit data below watermark LPUART_STAT_TDRE
// Transmit complete LPUART_STAT_TC
// Idle line LPUART_STAT_IDLE
// Receive data above watermark LPUART_STAT_RDRF
// LIN break detect UART_S2_LBKDIF
// RxD pin active edge UART_S2_RXEDGIF

void lpuart0_status_isr(void)
{
uint32_t head, tail, n;
uint32_t c;

if (LPUART0_STAT & LPUART_STAT_RDRF) {
// if (use9Bits && (UART5_C3 & 0x80)) {
// n = UART5_D | 0x100;
// } else {
// n = UART5_D;
// }
n = LPUART0_DATA & 0x3ff; // use only the 10 data bits
head = rx_buffer_head + 1;
if (head >= RX_BUFFER_SIZE) head = 0;
if (head != rx_buffer_tail) {
rx_buffer[head] = n;
rx_buffer_head = head;
}
if (rts_pin) {
int avail;
tail = tx_buffer_tail;
if (head >= tail) avail = head - tail;
else avail = RX_BUFFER_SIZE + head - tail;
if (avail >= RTS_HIGH_WATERMARK) rts_deassert();
}
}
c = LPUART0_CTRL;
if ((c & LPUART_CTRL_TIE) && (LPUART0_STAT & LPUART_STAT_TDRE)) {
head = tx_buffer_head;
tail = tx_buffer_tail;
if (head == tail) {
LPUART0_CTRL &= ~LPUART_CTRL_TIE;
LPUART0_CTRL |= LPUART_CTRL_TCIE; // Actually wondering if we can just leave this one on...
} else {
if (++tail >= TX_BUFFER_SIZE) tail = 0;
n = tx_buffer[tail];
//if (use9Bits) UART5_C3 = (UART5_C3 & ~0x40) | ((n & 0x100) >> 2);
LPUART0_DATA = n;
tx_buffer_tail = tail;
}
}
if ((c & LPUART_CTRL_TCIE) && (LPUART0_STAT & LPUART_STAT_TC)) {
transmitting = 0;
if (transmit_pin) transmit_deassert();
LPUART0_CTRL &= ~LPUART_CTRL_TCIE; // Actually wondering if we can just leave this one on...
}
}

#endif // HAS_KINETISK_UART4

Loading…
Cancel
Save