📄 dmx.c
字号:
/*
* Digital AC Dimmer
*
* Copyright (c) 2005-2006 Chris Fallin <chris@cfallin.org>
* Placed under the modified BSD license
*
* dmx.c: DMX-512 transmitter module
*/
#include "dmx.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
// PD0 (RXD) and PD1 (TXD) are connected to 75176's RXD and TXD; PD4 is
// connected to REN/TXEN (high = transmit)
static char levels[512];
static int channel_count;
// the channel to be transmitted when the UART is ready for the next char;
// a value of -1 indicates the start code
// additionally, a value of -3 indicates that the break is currently being
// transmitted, and -2 indicates that the mark-after-break is being
// transmitted
static int cur_channel;
void dmx_initialize(int n_chan)
{
channel_count = n_chan;
if(channel_count > 512)
channel_count = 512;
// initialize I/O pins
PORTD = PORTD & ~0x10; // PD4 = 1: 75176 always in transmit mode
DDRD = (DDRD & ~0x13) | 0x12; // PD1, PD4 out; PD0 in
// initialize the USART
UBRRH = 0;
UBRRL = 3; // gives 250kbps for a 16MHz system clock
UCSRA = 0x00;
UCSRB = 0xd8; // everything enabled except for UDR Empty interrupt
UCSRC = 0x8e; // 8 data bits, 2 stop bits
// intialize Timer 1
TCCR1A = 0x00;
TCCR1B = 0x03; // timer 1: CPU clock / 64 (1 tick = 1 bit time, or 4 us)
}
void dmx_set_level(int channel, char level)
{
if(channel >= channel_count)
return;
levels[channel] = level;
}
void dmx_start()
{
// start it off with the break
cur_channel = -3;
// disable the transmitter and set the pin low
UCSRB &= ~0x08;
PORTD &= ~0x02;
// start the timer
OCR1A = 21; // 88 us, or 22 bit times
TCNT1 = 0; // start the timer
TIMSK |= 0x10; // enable the int
}
SIGNAL(SIG_UART_TRANS)
{
// sanity check
if(cur_channel < 0)
return;
// if we've reached the end of our data, start the whole thing over again
if(cur_channel >= channel_count)
{
dmx_start();
return;
}
UDR = levels[cur_channel];
cur_channel++;
}
SIGNAL(SIG_UART_RECV)
{
char rx_byte;
// discard the received byte and return
rx_byte = UDR;
}
SIGNAL(SIG_OUTPUT_COMPARE1A)
{
if(cur_channel == -3)
{
// we were transmitting the break; this interrupt ends it
// re-enable the transmitter
UCSRB |= 0x08;
// now we move on to the mark-after-break: 8 us of idle
cur_channel = -2;
TCNT1 = 0;
OCR1A = 1; // 8 us, or 2 bit times
return;
}
else if(cur_channel == -2)
{
// we were in the mark-after-brea; this interrupt ends it
// transmit the start code (always zero in our implementation: no custom
// non-dimmer data packets)
UDR = 0;
// when that's done, we start with the channel data
cur_channel = 0;
return;
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -