2014-git-work/arduino/libraries/SoftModem/.svn/text-base/SoftModem.cpp.svn-base
2013-02-26 00:38:35 +09:00

326 lines
7.4 KiB
Text

#include "SoftModem.h"
#define TX_PIN (3)
#define RX_PIN1 (6) // AIN0
#define RX_PIN2 (7) // AIN1
SoftModem *SoftModem::activeObject = 0;
SoftModem::SoftModem() {
}
SoftModem::~SoftModem() {
end();
}
#if F_CPU == 16000000
#if SOFT_MODEM_BAUD_RATE <= 126
#define TIMER_CLOCK_SELECT (7)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(1024))
#elif SOFT_MODEM_BAUD_RATE <= 315
#define TIMER_CLOCK_SELECT (6)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(256))
#elif SOFT_MODEM_BAUD_RATE <= 630
#define TIMER_CLOCK_SELECT (5)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(128))
#elif SOFT_MODEM_BAUD_RATE <= 1225
#define TIMER_CLOCK_SELECT (4)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(64))
#else
#define TIMER_CLOCK_SELECT (3)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(32))
#endif
#else
#if SOFT_MODEM_BAUD_RATE <= 126
#define TIMER_CLOCK_SELECT (6)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(256))
#elif SOFT_MODEM_BAUD_RATE <= 315
#define TIMER_CLOCK_SELECT (5)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(128))
#elif SOFT_MODEM_BAUD_RATE <= 630
#define TIMER_CLOCK_SELECT (4)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(64))
#else
#define TIMER_CLOCK_SELECT (3)
#define MICROS_PER_TIMER_COUNT (clockCyclesToMicroseconds(32))
#endif
#endif
#define BIT_PERIOD (1000000/SOFT_MODEM_BAUD_RATE)
#define HIGH_FREQ_MICROS (1000000/SOFT_MODEM_HIGH_FREQ)
#define LOW_FREQ_MICROS (1000000/SOFT_MODEM_LOW_FREQ)
#define HIGH_FREQ_CNT (BIT_PERIOD/HIGH_FREQ_MICROS)
#define LOW_FREQ_CNT (BIT_PERIOD/LOW_FREQ_MICROS)
#define MAX_CARRIR_BITS (40000/BIT_PERIOD)
#define TCNT_BIT_PERIOD (BIT_PERIOD/MICROS_PER_TIMER_COUNT)
#define TCNT_HIGH_FREQ (HIGH_FREQ_MICROS/MICROS_PER_TIMER_COUNT)
#define TCNT_LOW_FREQ (LOW_FREQ_MICROS/MICROS_PER_TIMER_COUNT)
#define TCNT_HIGH_TH_L (TCNT_HIGH_FREQ * 0.90)
#define TCNT_HIGH_TH_H (TCNT_HIGH_FREQ * 1.15)
#define TCNT_LOW_TH_L (TCNT_LOW_FREQ * 0.85)
#define TCNT_LOW_TH_H (TCNT_LOW_FREQ * 1.10)
#if SOFT_MODEM_DEBUG_ENABLE
static volatile uint8_t *_portLEDReg;
static uint8_t _portLEDMask;
#endif
enum { START_BIT = 0, DATA_BIT = 8, STOP_BIT = 9, INACTIVE = 0xff };
void SoftModem::begin(void)
{
pinMode(RX_PIN1, INPUT);
digitalWrite(RX_PIN1, LOW);
pinMode(RX_PIN2, INPUT);
digitalWrite(RX_PIN2, LOW);
pinMode(TX_PIN, OUTPUT);
digitalWrite(TX_PIN, LOW);
_txPortReg = portOutputRegister(digitalPinToPort(TX_PIN));
_txPortMask = digitalPinToBitMask(TX_PIN);
#if SOFT_MODEM_DEBUG_ENABLE
_portLEDReg = portOutputRegister(digitalPinToPort(13));
_portLEDMask = digitalPinToBitMask(13);
pinMode(13, OUTPUT);
#endif
_recvStat = INACTIVE;
_recvBufferHead = _recvBufferTail = 0;
SoftModem::activeObject = this;
_lastTCNT = TCNT2;
_lastDiff = _lowCount = _highCount = 0;
TCCR2A = 0;
TCCR2B = TIMER_CLOCK_SELECT;
ACSR = _BV(ACIE) | _BV(ACIS1);
DIDR1 = _BV(AIN1D) | _BV(AIN0D);
}
void SoftModem::end(void)
{
ACSR &= ~(_BV(ACIE));
TIMSK2 &= ~(_BV(OCIE2A));
DIDR1 &= ~(_BV(AIN1D) | _BV(AIN0D));
SoftModem::activeObject = 0;
}
void SoftModem::demodulate(void)
{
uint8_t t = TCNT2;
uint8_t diff;
diff = t - _lastTCNT;
if(diff < 4)
return;
_lastTCNT = t;
if(diff > (uint8_t)(TCNT_LOW_TH_H))
return;
// 移動平均
_lastDiff = (diff >> 1) + (diff >> 2) + (_lastDiff >> 2);
if(_lastDiff >= (uint8_t)(TCNT_LOW_TH_L)){
_lowCount += _lastDiff;
if(_recvStat == INACTIVE){
// スタートビット検出
if(_lowCount >= (uint8_t)(TCNT_BIT_PERIOD * 0.5)){
_recvStat = START_BIT;
_highCount = 0;
_recvBits = 0;
OCR2A = t + (uint8_t)(TCNT_BIT_PERIOD) - _lowCount;
TIFR2 |= _BV(OCF2A);
TIMSK2 |= _BV(OCIE2A);
}
}
}
else if(_lastDiff <= (uint8_t)(TCNT_HIGH_TH_H)){
if(_recvStat == INACTIVE){
_lowCount = 0;
_highCount = 0;
}
else{
_highCount += _lastDiff;
}
}
}
// アナログコンパレータ割り込み
ISR(ANALOG_COMP_vect)
{
SoftModem::activeObject->demodulate();
}
void SoftModem::recv(void)
{
uint8_t high;
// ビット論理判定
if(_highCount > _lowCount){
_highCount = 0;
high = 0x80;
}
else{
_lowCount = 0;
high = 0x00;
}
// スタートビット受信
if(_recvStat == START_BIT){
if(!high){
_recvStat++;
}
else{
goto end_recv;
}
}
// データビット受信
else if(_recvStat <= DATA_BIT) {
_recvBits >>= 1;
_recvBits |= high;
_recvStat++;
}
// ストップビット受信
else if(_recvStat == STOP_BIT){
if(high){
// 受信バッファに格納
uint8_t new_tail = (_recvBufferTail + 1) & (SOFT_MODEM_RX_BUF_SIZE - 1);
if(new_tail != _recvBufferHead){
_recvBuffer[_recvBufferTail] = _recvBits;
_recvBufferTail = new_tail;
}
else{
;// オーバーランエラー
}
}
else{
;// フレミングエラー
}
goto end_recv;
}
else{
end_recv:
_recvStat = INACTIVE;
TIMSK2 &= ~_BV(OCIE2A);
}
}
// タイマー2比較一致割り込みA
ISR(TIMER2_COMPA_vect)
{
OCR2A += (uint8_t)TCNT_BIT_PERIOD;
SoftModem::activeObject->recv();
#if SOFT_MODEM_DEBUG_ENABLE
*_portLEDReg ^= _portLEDMask;
#endif
}
int SoftModem::available()
{
return (_recvBufferTail + SOFT_MODEM_RX_BUF_SIZE - _recvBufferHead) & (SOFT_MODEM_RX_BUF_SIZE - 1);
}
int SoftModem::read()
{
if(_recvBufferHead == _recvBufferTail)
return -1;
int d = _recvBuffer[_recvBufferHead];
_recvBufferHead = (_recvBufferHead + 1) & (SOFT_MODEM_RX_BUF_SIZE - 1);
return d;
}
int SoftModem::peek()
{
if(_recvBufferHead == _recvBufferTail)
return -1;
return _recvBuffer[_recvBufferHead];
}
void SoftModem::flush()
{
}
void SoftModem::modulate(uint8_t b)
{
uint8_t cnt,tcnt,tcnt2;
if(b){
cnt = (uint8_t)(HIGH_FREQ_CNT);
tcnt2 = (uint8_t)(TCNT_HIGH_FREQ / 2);
tcnt = (uint8_t)(TCNT_HIGH_FREQ) - tcnt2;
}else{
cnt = (uint8_t)(LOW_FREQ_CNT);
tcnt2 = (uint8_t)(TCNT_LOW_FREQ / 2);
tcnt = (uint8_t)(TCNT_LOW_FREQ) - tcnt2;
}
do {
cnt--;
{
OCR2B += tcnt;
TIFR2 |= _BV(OCF2B);
while(!(TIFR2 & _BV(OCF2B)));
}
*_txPortReg ^= _txPortMask;
{
OCR2B += tcnt2;
TIFR2 |= _BV(OCF2B);
while(!(TIFR2 & _BV(OCF2B)));
}
*_txPortReg ^= _txPortMask;
} while (cnt);
}
// Preamble bit before transmission
// 1 start bit (LOW)
// 8 data bits, LSB first
// 1 stop bit (HIGH)
// ...
// Postamble bit after transmission
size_t SoftModem::write(const uint8_t *buffer, size_t size)
{
// プリアンブルビット
uint8_t cnt = ((micros() - _lastWriteTime) / BIT_PERIOD) + 1;
if(cnt > MAX_CARRIR_BITS){
cnt = MAX_CARRIR_BITS;
}
for(uint8_t i = 0; i<cnt; i++){
modulate(HIGH);
}
size_t n = size;
while (size--) {
uint8_t data = *buffer++;
// スタート1ビット
modulate(LOW);
// データ8ビット
for(uint8_t mask = 1; mask; mask <<= 1){
if(data & mask){
modulate(HIGH);
}
else{
modulate(LOW);
}
}
// ストップ1ビット
modulate(HIGH);
}
// ポストアンブルビット
modulate(HIGH);
_lastWriteTime = micros();
return n;
}
size_t SoftModem::write(uint8_t data)
{
return write(&data, 1);
}