Arduino Loconet Example1From SPCoast[edit] Arduino demo sketch showing LocoNet Receive functionalityThis sketch is hardcoded to use ICP pin 8 (port PINB bit PB0) for input from LocoNet via a "usual" LM311 or LM393 comparator circuit:
<code>
// Copyright 2008, John Plocher and Stefan Bormann, GPL v2.1 or later
#include <SoftwareSerial.h>
#include <avr/wdt.h>
#include <avr/io.h>
#include <stdint.h>
// #define LNtxPin 6 // Loconet Transmit is not implemented yet
void setup() {
LnRxOnlyInit();
pinMode(13, OUTPUT); // Flash LED to show progress
digitalWrite(13, 0);
Serial.begin(9600); // set up Serial library at 9600 bps
Serial.println("Loconet read test");
}
void loop() {
unsigned char opcode = LnRxOnlyOpcode();
unsigned char opcodefamily = (opcode >> 5);
switch (opcodefamily) {
case B100: // 2 data bytes, inc checksum
get_packet(opcode, 2); break;
case B101: // 4 data bytes, inc checksum
get_packet(opcode, 4); break;
case B110: // 6 data bytes, inc checksum
get_packet(opcode, 6); break;
case B111: // N data bytes, inc checksum, next octet is N
int N = LnRxOnlyData();
get_packet(opcode, N-1);
break;
}
}
// Read a N-sized packet
// 1st byte already read, last is checksum
void get_packet(unsigned char opcode, int n) {
unsigned char d[20];
d[0] = (unsigned char) n;
d[1] = opcode;
for (unsigned char x = 2; x <= n; x++) {
d[x] = LnRxOnlyData();
}
Serial.print("Packet["); Serial.print((int)d[0]); Serial.print("]: ");
for (unsigned char x = 1; x <= d[0]; x++) {
Serial.print(" 0x");
Serial.print((int)d[x], HEX);
}
Serial.println("");
}
// ==== Originally from Embedded Loconet: LnRxOnlySw.[hc] ====
// ==== 17-Mar-2006 version by Stefan Bormann ====
// ==== Modified 02-Dec-2008 by John Plocher for Arduino use ====
/****************************************************************************
Copyright (C) 2006 Stefan Bormann
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
*****************************************************************************
IMPORTANT:
Note: The sale any LocoNet device hardware (including bare PCB's) that
uses this or any other LocoNet software, requires testing and certification
by Digitrax Inc. and will be subject to a licensing agreement.
Please contact Digitrax Inc. for details.
*****************************************************************************/
#define LN_RX_PORT PINB
#define LN_RX_BIT PB0
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
// define the timer interrupt flag register if necessary
#if !defined(TIFR)
#if defined(TIFR1)
#define TIFR TIFR1
#else
#error AVR device not supported
#endif
#endif
#define LN_BIT_PERIOD (F_CPU / 16666)
// The Start Bit period is a full bit period + half of the next bit period
// so that the bit is sampled in middle of the bit
#define LN_TIMER_RX_START_PERIOD LN_BIT_PERIOD + (LN_BIT_PERIOD / 2)
#define LN_TIMER_RX_RELOAD_PERIOD LN_BIT_PERIOD
unsigned short lnCompareTarget; // calculated bit sample time
unsigned char ucData; // one received message byte
unsigned char ucLnRxOnlyChecksum; // non static for access from inline function
static void inline LnRxOnlyInit(void)
{
pinMode(8, INPUT); // LocoNet on ICP, PB0
TCCR1A = 0;
TCCR1B = 0x01; // no prescaler, normal mode
}
void LnRxOnlySevereError(void)
{
Serial.println("Loconet ERROR");
digitalWrite(13, 1);
wdt_enable(WDTO_1S); // prepare for reset
while (1) {} // stop
}
unsigned char LnRxOnlyOpcode(void)
{
do {
ucLnRxOnlyChecksum = 0;
loop_until_bit_is_set (LN_RX_PORT, LN_RX_BIT); // wait for IDLE=1 state
LnRxOnlyData();
} while (bit_is_clear(ucData, 7)); // loop if not opcode or byte framing error
return ucData;
}
unsigned char LnRxOnlyData(void)
{
unsigned char ucLoop;
//--- wait for start bit
digitalWrite(13, 0);
loop_until_bit_is_clear(LN_RX_PORT, LN_RX_BIT);
digitalWrite(13, 1);
//--- Get the Current Timer1 Count and Add the offset for the Compare target to find the middle of the first data bit
lnCompareTarget = TCNT1 + LN_TIMER_RX_START_PERIOD;
OCR1A = lnCompareTarget ;
//--- Loop over data bits
for (ucLoop=0; ucLoop<8; ucLoop++) {
//--- Waiting for data bit
ucData >>= 1; // Loconet is LSB first, shifting data in from left
digitalWrite(13, 0);
sbi(TIFR, OCF1A);
loop_until_bit_is_set(TIFR, OCF1A); // wait for middle of data bit
digitalWrite(13, 1);
//--- Copy data bit from input to MSB of data buffer
if (bit_is_set(LN_RX_PORT, LN_RX_BIT)) {
sbi(ucData, 7);
}
//--- Calculate timer target for next data (or stop) bit
lnCompareTarget += LN_TIMER_RX_RELOAD_PERIOD;
OCR1A = lnCompareTarget;
}
//--- Check stop bit
digitalWrite(13, 0);
sbi(TIFR, OCF1A);
loop_until_bit_is_set(TIFR, OCF1A); // wait for middle of stop bit
digitalWrite(13, 1);
if (bit_is_clear(LN_RX_PORT, LN_RX_BIT)) {
LnRxOnlySevereError();
}
ucLnRxOnlyChecksum ^= ucData;
digitalWrite(13, 0);
return ucData;
}
void LnRxOnlyChecksum(void)
{
LnRxOnlyData();
if (ucLnRxOnlyChecksum!=0xff)
LnRxOnlySevereError();
}
</code>
|

