/*
HSTSerial.h - Hi Speed TX Serial Library for ATSAMD21
Baudrate: 48MHz: 115200 -12M bps
Exsample:
#include "HSTSerial.h"
#define TX_PORT (32 * 1 + 8) // PB08_A6_D6_TX
#define HSTX HSTSerial<921600, TX_PORT>
void setup(void)
{
HSTX::begin();
}
void loop(void)
{
HSTX::write(0x41);
}
Copyright (c) 2020 Sasapea's Lab. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library; if not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
*/
#ifndef __HSTSERIAL_H
#define __HSTSERIAL_H
#include "IOBUS.h"
#define HSTSerial_115200 115500
#define HSTSerial_230400 232000
#define HSTSerial_320400 324000
#define HSTSerial_460800 466350
#define HSTSerial_921600 941500
#define HSTSerial_1M 1021000
#define HSTSerial_2M 2090000
#define HSTSerial_3M 3200000
#define HSTSerial_4M 4370000
#define HSTSerial_5M 5550000
#define HSTSerial_6M 6500000
#define HSTSerial_8M 8200000
#define HSTSerial_10M 10000000
#define HSTSerial_12M 11300000
#define HSTSerial_POS(pin) (pin & 31)
#define HSTSerial_PORT(pin) (int)(&PORT_IOBUS->Group[pin >> 5])
#define HSTSerial_OUTCLR(pin) (int)(&((PortGroup *)0)->OUTCLR)
#define HSTSerial_OUTSET(pin) (int)(&((PortGroup *)0)->OUTSET)
#define HSTSerial_OUTTGL(pin) (int)(&((PortGroup *)0)->OUTTGL)
#define HSTSerial_DELAY_CYCLE 3 // cycle of delay loop
#define HSTSerial_TXBIT_CYCLE 4 // 3 + (1)???
#define HSTSerial_BIT_CYCLE ((float)F_CPU / BAUDRATE)
#define HSTSerial_CPU_CYCLE (int)(HSTSerial_BIT_CYCLE + 0.5)
#define HSTSerial_DELAY(n) \
do \
{ \
int cycle = 0; \
float error = HSTSerial_BIT_CYCLE - HSTSerial_CPU_CYCLE; \
float round = (error < 0 ? -0.5 : 0.5); \
error -= (int)error; \
if (((int)(error * (n) + round) ^ (int)(error * ((n) + 1) + round)) & 1) \
cycle += (error < 0 ? -1 : 1); \
if ((cycle += HSTSerial_CPU_CYCLE - HSTSerial_TXBIT_CYCLE) > 0) \
{ \
if (cycle / HSTSerial_DELAY_CYCLE) \
__asm__ volatile \
( \
"mov r3, %[_COUNT_]" "\n\t" \
"1: sub r3, #1" "\n\t" \
"bne 1b" "\n\t" \
: \
: [_COUNT_] "I" (cycle / HSTSerial_DELAY_CYCLE) \
: "r3" \
); \
if ((cycle % HSTSerial_DELAY_CYCLE) > 0) \
__asm__ volatile ("nop"); \
if ((cycle % HSTSerial_DELAY_CYCLE) > 1) \
__asm__ volatile ("nop"); \
} \
} \
while (0)
template<uint32_t BAUDRATE, uint32_t PIN>
class HSTSerial
{
private:
public:
static void begin(bool drvstr = true)
{
IOBUS::pinMode(PIN, OUTPUT, drvstr);
}
static void write(uint8_t data)
{
if (HSTSerial_CPU_CYCLE < 256 * HSTSerial_DELAY_CYCLE + HSTSerial_TXBIT_CYCLE - 1)
{
__asm__ volatile
(
"mov r0, %[_DATA_]" "\n\t"
"add r1, r0, r0" "\n\t"
"eor r0, r0, r1" "\n\t"
"mov r1, #1" "\n\t"
"lsl r1, r1, %[_POS_]" "\n\t"
"mov r2, %[_PORT_]" "\n\t"
"lsl r2, r2, #24" "\n\t"
"mov r3, %[_PGRP_]" "\n\t"
"lsl r3, r3, #7" "\n\t"
"add r2, r2, r3" "\n\t"
"CPSID i" "\n\t"
"str r1, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (PIN & 31)
, [_REG_ ] "I" (HSTSerial_OUTCLR(PIN))
, [_PORT_] "I" (HSTSerial_PORT(PIN) >> 24)
, [_PGRP_] "I" ((HSTSerial_PORT(PIN) >> 7) & 0xFF)
, [_DATA_] "r" (data)
: "r1", "r2", "r3"
);
HSTSerial_DELAY(0);
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (PIN & 31)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
HSTSerial_DELAY(1);
if ((PIN & 31) >= 1)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 1)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (1 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(2);
if ((PIN & 31) >= 2)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 2)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (2 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(3);
if ((PIN & 31) >= 3)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 3)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (3 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(4);
if ((PIN & 31) >= 4)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 4)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (4 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(5);
if ((PIN & 31) >= 5)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 5)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (5 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(6);
if ((PIN & 31) >= 6)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 6)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (6 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(7);
if ((PIN & 31) >= 7)
{
__asm__ volatile
(
"lsl r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" ((PIN & 31) - 7)
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
else
{
__asm__ volatile
(
"lsr r3, r0, %[_POS_]" "\n\t"
"and r3, r3, r1" "\n\t"
"str r3, [r2, %[_REG_]]" "\n\t"
:
: [_POS_ ] "I" (7 - (PIN & 31))
, [_REG_ ] "I" (HSTSerial_OUTTGL(PIN))
: "r3"
);
}
HSTSerial_DELAY(8);
__asm__ volatile
(
"nop" "\n\t"
"nop" "\n\t"
"str r1, [r2, %[_REG_]]" "\n\t"
"CPSIE i" "\n\t"
:
: [_REG_ ] "I" (HSTSerial_OUTSET(PIN))
);
HSTSerial_DELAY(9);
}
}
static void write(uint8_t *buf, uint32_t len)
{
while (len--)
write(*buf++);
}
};
#endif