2015-10-26 19:39:13 +01:00
|
|
|
// Half-duplex interrupt based serial port.
|
|
|
|
//
|
2015-12-31 10:20:50 +01:00
|
|
|
// Copyright 2015 Google Inc.
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2015-10-25 22:06:31 +01:00
|
|
|
#include "serial.h"
|
|
|
|
|
|
|
|
#include <avr/io.h>
|
|
|
|
#include <avr/interrupt.h>
|
2015-10-26 19:39:13 +01:00
|
|
|
#include <avr/sleep.h>
|
2015-10-25 22:06:31 +01:00
|
|
|
|
|
|
|
Serial* Serial::instance_;
|
|
|
|
|
|
|
|
#define _clear(port, pin) port &= ~_BV(pin)
|
|
|
|
#define _set(port, pin) port |= _BV(pin)
|
|
|
|
|
2015-12-31 10:34:34 +01:00
|
|
|
/// Initialise the hardware.
|
2015-10-25 22:06:31 +01:00
|
|
|
void Serial::init() {
|
|
|
|
state_ = Idle;
|
|
|
|
rx_full_ = false;
|
|
|
|
|
|
|
|
instance_ = this;
|
|
|
|
|
2015-10-26 19:39:13 +01:00
|
|
|
// Set up the GPIOs.
|
|
|
|
_set(DDRB, TxPin);
|
|
|
|
_set(PORTB, TxPin);
|
|
|
|
_clear(DDRB, RxPin);
|
2015-10-25 22:06:31 +01:00
|
|
|
|
2015-10-26 19:39:13 +01:00
|
|
|
// Set up the timer.
|
|
|
|
static_assert(BitTime > 50, "BitTime is too short");
|
|
|
|
static_assert(BitTime < 255*2/3, "BitTime may overflow");
|
2015-10-25 22:06:31 +01:00
|
|
|
OCR1C = BitTime;
|
2015-10-26 19:39:13 +01:00
|
|
|
TCCR1 = _BV(CTC1) | _BV(PWM1A);
|
2015-10-25 22:06:31 +01:00
|
|
|
|
2015-10-26 19:39:13 +01:00
|
|
|
static_assert(Prescale == 2, "Mismatched prescaler");
|
|
|
|
TCCR1 |= _BV(CS11);
|
|
|
|
|
|
|
|
// Set up the pin change interrupt.
|
2015-10-25 22:06:31 +01:00
|
|
|
PCMSK = _BV(RxPin);
|
|
|
|
_set(GIMSK, PCIE);
|
|
|
|
}
|
|
|
|
|
2015-12-31 10:34:34 +01:00
|
|
|
/// Handle the pin change interrupt by starting receive.
|
2015-10-25 22:06:31 +01:00
|
|
|
void Serial::pcint0() {
|
|
|
|
_clear(PCMSK, RxPin);
|
|
|
|
_clear(TIMSK, TOV1); // PENDING
|
|
|
|
|
|
|
|
OCR1C = (BitTime / 2 * 3) - (PCIntDelay / Prescale);
|
|
|
|
TCNT1 = 0;
|
|
|
|
_set(TIFR, TOV1);
|
|
|
|
|
|
|
|
state_ = Receive;
|
|
|
|
bits_ = 8;
|
|
|
|
|
|
|
|
_set(TIMSK, TOV1);
|
|
|
|
}
|
|
|
|
|
2015-12-31 10:34:34 +01:00
|
|
|
/// Handle the timer overflow interrupt by doing the next bit.
|
2015-10-25 22:06:31 +01:00
|
|
|
inline void Serial::timer1ovf() {
|
|
|
|
switch (state_) {
|
|
|
|
case Receive: {
|
|
|
|
auto got = PINB;
|
|
|
|
OCR1C = BitTime;
|
|
|
|
if (bits_ > 0) {
|
|
|
|
rxing_ >>= 1;
|
|
|
|
if (got & _BV(RxPin)) {
|
|
|
|
rxing_ |= 0x80;
|
|
|
|
}
|
|
|
|
bits_--;
|
|
|
|
} else {
|
|
|
|
state_ = Idle;
|
|
|
|
rxed_ = rxing_;
|
|
|
|
rx_full_ = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Transmit:
|
|
|
|
if (txing_ & 1) {
|
|
|
|
_set(PORTB, TxPin);
|
|
|
|
} else {
|
|
|
|
_clear(PORTB, TxPin);
|
|
|
|
}
|
|
|
|
txing_ >>= 1;
|
|
|
|
if (--bits_ == 0) {
|
|
|
|
state_ = Idle;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state_ == Idle) {
|
|
|
|
_clear(TIMSK, TOV1);
|
|
|
|
_set(PCMSK, RxPin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-31 10:34:34 +01:00
|
|
|
/// Wait until the hardware is free then start the transmit.
|
2015-10-25 22:06:31 +01:00
|
|
|
void Serial::putch(uint8_t ch) {
|
|
|
|
while (state_ != Idle) {
|
2015-10-26 19:39:13 +01:00
|
|
|
sleep_cpu();
|
2015-10-25 22:06:31 +01:00
|
|
|
}
|
|
|
|
state_ = Transmit;
|
|
|
|
txing_ = (ch << 1) | 0xFE00;
|
|
|
|
bits_ = 10;
|
|
|
|
|
|
|
|
TCNT1 = 0;
|
|
|
|
_set(TIMSK, TOV1);
|
|
|
|
}
|
|
|
|
|
2015-12-31 10:34:34 +01:00
|
|
|
/// Send a string.
|
2015-10-26 19:39:13 +01:00
|
|
|
void Serial::putstr(const char* str) {
|
|
|
|
for (; *str != '\0'; str++) {
|
|
|
|
putch(*str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-31 10:34:34 +01:00
|
|
|
/// Wait until a character is received, then return it.
|
2015-10-25 22:06:31 +01:00
|
|
|
uint8_t Serial::getch() {
|
|
|
|
while (!rx_full_) {
|
2015-10-26 19:39:13 +01:00
|
|
|
sleep_cpu();
|
2015-10-25 22:06:31 +01:00
|
|
|
}
|
|
|
|
uint8_t ch = rxed_;
|
|
|
|
rx_full_ = false;
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void Serial::pcint0_bounce() { instance_->pcint0(); }
|
|
|
|
|
|
|
|
inline void Serial::timer1ovf_bounce() { instance_->timer1ovf(); }
|
|
|
|
|
|
|
|
ISR(TIMER1_OVF_vect) { Serial::timer1ovf_bounce(); }
|
|
|
|
|
|
|
|
ISR(PCINT0_vect) { Serial::pcint0_bounce(); }
|