Initial commit
This commit is contained in:
91
src/common.c
Normal file
91
src/common.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
#define UART_BAUDRATE 9600
|
||||
#define UART_TXBUF_SIZE 128
|
||||
|
||||
#define UART_TXBUF_MASK (UART_TXBUF_SIZE - 1)
|
||||
#define UART_BAUD_PRESCALE ((((F_CPU / 16) + \
|
||||
(UART_BAUDRATE / 2)) / (UART_BAUDRATE)) - 1)
|
||||
|
||||
static volatile char txbuf[UART_TXBUF_SIZE];
|
||||
static volatile word txhead, txtail;
|
||||
|
||||
static void UART_Init(void);
|
||||
static void UART_Puts(const char *str);
|
||||
static void UART_Putc(char ch);
|
||||
|
||||
void Info(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char msg[256];
|
||||
|
||||
// Lazily initialize this module
|
||||
if ((UCSR0B & BIT(TXEN0)) == 0) {
|
||||
UART_Init();
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(msg, sizeof(msg), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
UART_Puts("[CORE] ");
|
||||
UART_Puts(msg);
|
||||
UART_Puts("\r\n");
|
||||
}
|
||||
|
||||
static void UART_Init(void)
|
||||
{
|
||||
txhead = 0;
|
||||
txtail = 0;
|
||||
|
||||
UCSR0B = BIT(TXEN0); // Enable TX circuitry
|
||||
UCSR0C = BIT(UCSZ01) | BIT(UCSZ00); // 8-bit data, 1-bit stop, no parity
|
||||
UBRR0H = (UART_BAUD_PRESCALE >> 8); // Set baud rate upper byte
|
||||
UBRR0L = UART_BAUD_PRESCALE; // Set baud rate lower byte
|
||||
|
||||
sei();
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
static void UART_Puts(const char *str)
|
||||
{
|
||||
while (*str != '\0') {
|
||||
UART_Putc(*str++);
|
||||
}
|
||||
}
|
||||
|
||||
static void UART_Putc(char ch)
|
||||
{
|
||||
word head;
|
||||
|
||||
// Wrap around if end of buffer reached
|
||||
head = (txhead + 1) & UART_TXBUF_MASK;
|
||||
while (head == txtail); // Wait for space
|
||||
|
||||
txbuf[head] = ch;
|
||||
txhead = head;
|
||||
|
||||
// Enable interrupt
|
||||
UCSR0B |= BIT(UDRIE0);
|
||||
}
|
||||
|
||||
// Data register empty
|
||||
ISR(USART0_UDRE_vect)
|
||||
{
|
||||
word tail;
|
||||
|
||||
// Anything in TX buffer?
|
||||
if (txhead != txtail) {
|
||||
// Write next byte to data register
|
||||
tail = (txtail + 1) & UART_TXBUF_MASK;
|
||||
UDR0 = txbuf[tail];
|
||||
txtail = tail;
|
||||
} else {
|
||||
// Disable interrupt
|
||||
UCSR0B &= ~BIT(UDRIE0);
|
||||
}
|
||||
}
|
||||
21
src/common.h
Normal file
21
src/common.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef MAD_COMMON_H
|
||||
#define MAD_COMMON_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DDR(p) DDR ## p
|
||||
#define PIN(p) PIN ## p
|
||||
#define PORT(p) PORT ## p
|
||||
#define BIT(n) (0x1U << (n))
|
||||
#define UNUSED(s) (void)(s)
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned short word;
|
||||
|
||||
void Info(const char *fmt, ...);
|
||||
|
||||
#include <util/delay.h>
|
||||
#define Sleep(ms) _delay_ms(ms)
|
||||
|
||||
#endif // MAD_COMMON_H
|
||||
304
src/main.c
Normal file
304
src/main.c
Normal file
@@ -0,0 +1,304 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <avr/interrupt.h>
|
||||
|
||||
// Edge direction
|
||||
#define E_RISING 1
|
||||
#define E_FALLING 0
|
||||
|
||||
// Decoder state
|
||||
#define S_IDLE 0
|
||||
#define S_SYNC 1
|
||||
#define S_DATA 2
|
||||
|
||||
static volatile bool txready; // Aligned with clock?
|
||||
static volatile int rxstate; // Current encoder state
|
||||
|
||||
static volatile byte rxbuf[128]; // Data buffer for decoder
|
||||
static volatile byte * rxhead; // Write position for decoder
|
||||
static volatile byte * rxtail; // End of decoder buffer
|
||||
static volatile word edgecap; // Current edge capture time
|
||||
static volatile word edgedir; // Current edge direction
|
||||
static volatile int lastbit; // Previously read logic value
|
||||
static volatile word numsync; // Number of preamble bits read
|
||||
static volatile word numdata; // Number of data bits read
|
||||
static volatile bool needmid; // Expect short interval next
|
||||
|
||||
static void SendByte(byte data);
|
||||
static void WaitPulse(void);
|
||||
static void HandleEdge(void);
|
||||
static void Synchronize(void);
|
||||
static void ReadPayload(void);
|
||||
static void ReadShortPeriod(void);
|
||||
static void ReadLongPeriod(void);
|
||||
static void WriteBit(int val);
|
||||
|
||||
void RF_Init(void)
|
||||
{
|
||||
// Set pins for line coded data
|
||||
DDR(D) |= BIT(5); // Modulator
|
||||
DDR(B) &= ~BIT(5); // Demodulator
|
||||
|
||||
// Calculate end of decoder buffer
|
||||
rxtail = rxbuf + sizeof(rxbuf);
|
||||
|
||||
// Data rate = 4000 Hz
|
||||
// Baud rate = 8000 Hz (edge changes)
|
||||
// Bit period = 1/f = 1/4000 = 250us
|
||||
// T (mid-bit time) = 125us
|
||||
|
||||
// Initialize TIMER1 to generate encoder clock pulses
|
||||
// at half of bit period which equals mid-bit time T.
|
||||
|
||||
TIMSK1 = 0x00; // Disable timer interrupts
|
||||
TIFR1 = 0x27; // Clear all interrupt flags
|
||||
TCCR1B = 0x02; // Prescale /8 = 1MHz = 1us per step
|
||||
OCR1A = 125; // Generate interrupt every T steps
|
||||
TCNT1 = 0; // Reset counter value to zero
|
||||
TCCR1A = 0x00; // Timer not connected to port
|
||||
TCCR1C = 0x00; // Do not force compare match
|
||||
TIMSK1 = 0x02; // Enable compare interrupt
|
||||
|
||||
// Initialize TIMER3 to interrupt when a rising edge on
|
||||
// PB5 is detected and when the counter value overflows.
|
||||
|
||||
TIMSK3 = 0x00; // Disable timer interrupts
|
||||
TIFR3 = 0x27; // Clear all interrupt flags
|
||||
TCCR3B = 0x02; // Prescale /8 = 1MHz = 1us per step
|
||||
TCCR3B |= 0x40; // Trigger capture event on rising edge
|
||||
OCR3A = 0; // Not using output compare interrupt
|
||||
TCNT3 = 0; // Reset counter value to zero
|
||||
TCCR3A = 0x00; // Timer not connected to port
|
||||
TCCR3C = 0x00; // Do not force compare match
|
||||
TIMSK3 = 0x20; // Enable input capture interrupt
|
||||
TIMSK3 |= 0x01; // Enable overflow interrupt
|
||||
}
|
||||
|
||||
void RF_Transmit(const byte *data, int size)
|
||||
{
|
||||
const byte *head = data;
|
||||
const byte *tail = data + size;
|
||||
|
||||
// The preamble with its alternating symbols is
|
||||
// line coded with only the actual meat-and-potato
|
||||
// transitions in the middle of the bit period and
|
||||
// none of those pesky boundary transitions. This
|
||||
// makes it possible for the decoder to align the
|
||||
// clock phase before receiving any data.
|
||||
|
||||
// Preamble for clock synchronization
|
||||
SendByte(0xAA); // AA = 1010 1010
|
||||
|
||||
while (head < tail) {
|
||||
SendByte(*head++);
|
||||
}
|
||||
}
|
||||
|
||||
int RF_Receive(byte *data, int size)
|
||||
{
|
||||
int n = 0;
|
||||
|
||||
while (1) {
|
||||
if (n == size || n == (int) numdata) {
|
||||
break; // Finished copying
|
||||
}
|
||||
data[n] = rxbuf[n];
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
static void SendByte(byte data)
|
||||
{
|
||||
// Manchester code always has a transition at the
|
||||
// middle of each bit period and may (depending on
|
||||
// the information to be transmitted) have one at
|
||||
// the start of the period also. The direction of
|
||||
// the mid-bit transition indicates the data.
|
||||
|
||||
// Transitions at the period boundaries do not carry
|
||||
// information. They only place the signal in the
|
||||
// correct state to allow the mid-bit transition.
|
||||
|
||||
for (int bit = 0; bit < 8; bit++) {
|
||||
if (data & (0x80 >> bit)) {
|
||||
WaitPulse();
|
||||
PORT(D) &= ~BIT(5);
|
||||
WaitPulse();
|
||||
// Rising edge
|
||||
PORT(D) |= BIT(5);
|
||||
} else {
|
||||
WaitPulse();
|
||||
PORT(D) |= BIT(5);
|
||||
WaitPulse();
|
||||
// Falling edge
|
||||
PORT(D) &= ~BIT(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void WaitPulse(void)
|
||||
{
|
||||
txready = false;
|
||||
while (!txready);
|
||||
}
|
||||
|
||||
static void HandleEdge(void)
|
||||
{
|
||||
if (edgedir != E_RISING) {
|
||||
return; // Wrong edge
|
||||
}
|
||||
|
||||
rxstate = S_SYNC;
|
||||
numsync = 1;
|
||||
}
|
||||
|
||||
static void Synchronize(void)
|
||||
{
|
||||
// Preamble only has middle transitions
|
||||
if (edgecap < 200 || edgecap > 300) {
|
||||
rxstate = S_IDLE; // Wrong timing
|
||||
return;
|
||||
}
|
||||
|
||||
numsync++;
|
||||
if (numsync == 8) {
|
||||
rxstate = S_DATA;
|
||||
rxhead = rxbuf;
|
||||
needmid = false;
|
||||
numdata = 0;
|
||||
lastbit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ReadPayload(void)
|
||||
{
|
||||
if (edgecap >= 75 && edgecap <= 175) {
|
||||
ReadShortPeriod();
|
||||
return;
|
||||
}
|
||||
|
||||
if (edgecap >= 200 && edgecap <= 300) {
|
||||
ReadLongPeriod();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wrong timing
|
||||
rxstate = S_IDLE;
|
||||
}
|
||||
|
||||
static void ReadShortPeriod(void)
|
||||
{
|
||||
// The period length gives us enough information to
|
||||
// know what the bit value is without even looking
|
||||
// at the edge direction.
|
||||
|
||||
if (needmid) {
|
||||
WriteBit(lastbit);
|
||||
needmid = false;
|
||||
} else {
|
||||
needmid = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void ReadLongPeriod(void)
|
||||
{
|
||||
// If there was a boundary transition we must expect
|
||||
// to receive another transition after mid-bit time,
|
||||
// otherwise something went wrong...
|
||||
|
||||
if (needmid) {
|
||||
rxstate = S_IDLE;
|
||||
return;
|
||||
}
|
||||
|
||||
lastbit = !lastbit;
|
||||
WriteBit(lastbit);
|
||||
}
|
||||
|
||||
static void WriteBit(int val)
|
||||
{
|
||||
int bit;
|
||||
|
||||
if (rxhead == rxtail) {
|
||||
return; // Discard
|
||||
}
|
||||
|
||||
bit = numdata % 8;
|
||||
numdata++;
|
||||
|
||||
*rxhead &= ~(0x80 >> bit);
|
||||
*rxhead |= (val << (7 - bit));
|
||||
|
||||
if (bit == 7) {
|
||||
rxhead++;
|
||||
}
|
||||
}
|
||||
|
||||
// Encoder clock pulse
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
TCNT1 = 0;
|
||||
txready = true;
|
||||
}
|
||||
|
||||
// Decoder edge capture
|
||||
ISR(TIMER3_CAPT_vect)
|
||||
{
|
||||
TCNT3 = 0;
|
||||
edgecap = ICR3;
|
||||
edgedir = (PIN(B) & BIT(5)) ? 1 : 0;
|
||||
|
||||
// Must not simply toggle the edge direction bit since
|
||||
// we can miss very quick edge changes and run out of
|
||||
// sync with the actual port state.
|
||||
|
||||
TCCR3B = (edgedir) ? 0x02 : 0x42;
|
||||
|
||||
if (rxstate == S_IDLE) {
|
||||
HandleEdge();
|
||||
} else if (rxstate == S_SYNC) {
|
||||
Synchronize();
|
||||
} else if (rxstate == S_DATA) {
|
||||
ReadPayload();
|
||||
}
|
||||
}
|
||||
|
||||
// Decoder overflow
|
||||
ISR(TIMER3_OVF_vect)
|
||||
{
|
||||
TCNT3 = 0;
|
||||
edgecap = 0xFFFF;
|
||||
if (rxstate == S_SYNC) {
|
||||
Synchronize();
|
||||
} else if (rxstate == S_DATA) {
|
||||
ReadPayload();
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bool running = true;
|
||||
const byte testmsg[] = "FOOBAR";
|
||||
byte recvbuf[128];
|
||||
|
||||
RF_Init();
|
||||
sei();
|
||||
|
||||
while (running) {
|
||||
Info("Sending phase encoded message...");
|
||||
RF_Transmit(testmsg, sizeof(testmsg));
|
||||
|
||||
Sleep(1000);
|
||||
|
||||
if (RF_Receive(recvbuf, sizeof(recvbuf))) {
|
||||
Info("Received message '%s'.", recvbuf);
|
||||
}
|
||||
|
||||
Sleep(1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user