#include #include #include "phys-m644.hpp" /* ######### * DEFINES */ /// Value for USART baud rate registers in Mega644 (U2X off) #define PHYS_UBRRVAL (( F_CPU / ( 16UL * DMX_BAUD )) - 1) /// 8us between interrupts #define PHYS_TIMESTEP 8UL /// Value for Timer OCR (Prescaler 8) #define PHYS_OCRVAL (F_CPU / (8UL * (1000000UL / PHYS_TIMESTEP))) /* ######### * DEFINES */ #define PHYS_TOSTRING(x) #x #define PHYS_MEMBER_ISR(name, vector) \ void name() __asm__(PHYS_TOSTRING(vector)) __attribute__((__signal__, __used__, __externally_visible__)) class Phys_m644::interrupt_container { static Phys_m644 *owner; static PHYS_MEMBER_ISR(on_udre, USART0_UDRE_vect); static PHYS_MEMBER_ISR(on_oc0a, TIMER0_COMPA_vect); static PHYS_MEMBER_ISR(on_oc2a, TIMER2_COMPA_vect); static PHYS_MEMBER_ISR(on_pcint3, PCINT3_vect); static PHYS_MEMBER_ISR(on_rxc, USART0_RX_vect); public: static void record(Phys_m644 *owner); }; Phys_m644 *Phys_m644::interrupt_container::owner = 0; void Phys_m644::interrupt_container::record(Phys_m644 *owner) { interrupt_container::owner = owner; } void Phys_m644::interrupt_container::on_udre() { owner->tx_update_state(); } void Phys_m644::interrupt_container::on_oc0a() { owner->tx_elapsed_us += PHYS_TIMESTEP; owner->tx_update_state(); } void Phys_m644::interrupt_container::on_oc2a() { owner->rx_elapsed_us += PHYS_TIMESTEP; } void Phys_m644::interrupt_container::on_pcint3() { owner->rx_update_state_pcint(); } void Phys_m644::interrupt_container::on_rxc() { owner->rx_update_state_uart(); } Phys_m644::Phys_m644(uint8_t tx_startcode) : tx_state(DMX_BREAK), tx_startcode(tx_startcode), tx_pointer(&(tx_data[0])), tx_elapsed_us(0), rx_state(DMX_BREAK), rx_startcode(0x00), rx_pointer(&(rx_data[0])), rx_elapsed_us(0) { // Initialize members interrupt_container::record(this); // data stores for (uint16_t addr = 0; addr < 512; addr++) { this->tx_data[addr] = 0x00; } // Initialize Periphery // Pin D1 (TXD0) is digital output // Pin D2 (PCINT26) is digital input DDRD = (0 << PD2) | (1 << PD1); // use pullup on PD2 PORTD = (1 << PD2); // enable PCINT on PD2 (PCINT26) PCICR = (1 << PCIE3); PCMSK3 = (1 << PCINT26); // Initialize USART0 // baud UBRR0H = (uint8_t) (PHYS_UBRRVAL >> 8); UBRR0L = (uint8_t) PHYS_UBRRVAL; // frame format: 8n2 UCSR0C = (1 << USBS0) | (1 << UCSZ01) | (1 << UCSZ00); // enable UDRE, RXCIE interrupts UCSR0B = (1 << RXCIE0) | (1 << UDRIE0); // Initialize Timer0 // prescaler: 8 TCCR0B = (1 << CS01); // mode: CTC TCCR0A = (1 << WGM01); OCR0A = PHYS_OCRVAL; // Initialize Timer2 // prescaler: 8 TCCR2B = (1 << CS21); // mode: CTC TCCR2A = (1 << WGM21); OCR2A = PHYS_OCRVAL; } void Phys_m644::set(uint16_t addr, uint8_t value) { this->tx_data[addr] = value; } Phys_m644::~Phys_m644() { this->tx_disable(); this->tx_timer_stop(); } void Phys_m644::tx_enable() { // enable TX UCSR0B |= (1 << TXEN0); } void Phys_m644::tx_disable() { // disable TX UCSR0B &= ~(1 << TXEN0); } void Phys_m644::rx_enable() { // enable RX UCSR0B |= (1 << RXEN0); } void Phys_m644::rx_disable() { // disable RX UCSR0B &= ~(1 << RXEN0); } void Phys_m644::tx_timer_start() { // reset elapsed time this->tx_elapsed_us = 0; // enable OCR interrupt TIMSK0 |= (1 << OCIE0A); } void Phys_m644::tx_timer_stop() { // disable OCR interrupt TIMSK0 &= ~(1 << OCIE0A); } void Phys_m644::rx_timer_start() { // reset elapsed time this->rx_elapsed_us = 0; // enable OCR interrupt TIMSK2 |= (1 << OCIE2A); } void Phys_m644::rx_timer_stop() { // disable OCR interrupt TIMSK2 &= ~(1 << OCIE2A); } void Phys_m644::tx_update_state() { switch (this->tx_state) { case DMX_BREAK: this->tx_disable(); // start break PORTD = (0 << PD1); this->tx_timer_start(); this->tx_state = DMX_MAB; break; case DMX_MAB: if (this->tx_elapsed_us < 176) { return; } // start MAB PORTD = (1 << PD1); this->tx_timer_start(); this->tx_state = DMX_START; break; case DMX_START: if (this->tx_elapsed_us < 16) { return; } this->tx_timer_stop(); this->tx_state = DMX_DATA; this->tx_pointer = &(this->tx_data[0]); this->tx_enable(); UDR0 = this->tx_startcode; break; case DMX_DATA: auto udrval = *(this->tx_pointer++); if (this->tx_pointer == &(this->tx_data[DMX_UNIVERSE])) { this->tx_state = DMX_BREAK; } UDR0 = udrval; break; } } void Phys_m644::rx_update_state_pcint() { switch (this->rx_state) { case DMX_BREAK: if(PIND & (1 << PD2)) { // PD2 is high, so not a break return; } // possible break: measure time this->rx_timer_start(); this->rx_state = DMX_MAB; break; case DMX_MAB: if (this->rx_elapsed_us < 88) { // break not long enough this->rx_state = DMX_BREAK; return; } // break detected, toggle LEDs PORTC ^= 0xFF; this->rx_state = DMX_START; break; default: // ignore pin change this->rx_state = DMX_BREAK; break; } } void Phys_m644::rx_update_state_uart() { switch (this->rx_state) { default: // wait for next break, for now this->rx_state = DMX_BREAK; // invalidate received byte auto narf = UDR0; (void) narf; break; } } void Phys_m644::start() { this->tx_state = DMX_BREAK; this->tx_update_state(); this->rx_enable(); } void Phys_m644::stop() { this->tx_disable(); }