/* avriom16.c
*
* code to drive the Olimex AVR-IO-M16 relay dev board
* this code is part of a project outlined on:
* http://sawdust.see-do.org/power/files/AVRRemoteACControl.html
*
* to make a network accessible AC outlet controller.
*
* this functionality will accept commands from the serial
* port and also tie any of the 4 port inputs to their corresponding
* outputs so if the input goes high, the relay will fire.
*
* this way the device can be driven either through GPIO from a micro
* or wireless comm device like XBee or LANtronix, or through a direct
* connection to a serial device or terminal.
*
* Copyright (C) 2007, 360VL, Inc. All rights reserved
* Copyright (C) 2007, Landon Cox. All rights reserved
*
* This code is licensed per GNU Affero 3 open source licensing.
*/
#include
#include
#include
#include
#include "delay.h"
#include "bits.h"
// AVR-IO-M16 related defines - derived from the Olimex schematic
#define LEDPORT PORTD
#define LED (1<<PD7)
#define RELAYPORT PORTB
#define RELAY1 (1<<PB3)
#define RELAY2 (1<<PB2)
#define RELAY3 (1<<PB1)
#define RELAY4 (1<<PB0)
#define IN1PORT PORTD
#define IN1 (1<<PD2)
#define IN2PORT PORTA
#define IN2 (1<<PA0)
#define IN3PORT PORTA
#define IN3 (1<<PA1)
#define IN4PORT PORTA
#define IN4 (1<<PA2)
// not sure what the fastest baud rate is for AVR mega16 running at 16MHz
// but this works and is fast enough for what we need to do...occasionally flip a relay switch
#define BAUD_RATE 38400ul
// #define FOSC 1000000UL
// #define FOSC 8000000UL
#define UBBR_VAL ((FOSC/16/BAUD_RATE)-1)
// FOSC is now override defined in makefile
// FOSC of 8000000UL (8 MHz) which is the max of the ATMega8 Olimex AVR-P28 board assumes
// the fuse bits as follows:
//
// CKOPT 1
// CKSEL3 1
// CKSEL2 1
// CKSEL1 1
// CKSEL0 1
// SUT1 1
// SUT0 0
//
// In AVR fuse parlance, a bit value of 1 is "unprogrammed" and a bit value of 0 is "programmed".
// In PonyProg fuse checkbox dialog, an unprogrammed
// those fuse bits will cause the crystal oscillator to be used as the clock source
// so, FOSC must match whatever you use for clock source.
//
// default ATMega8 settings are:
// CKOPT 1
// CKSEL3 0
// CKSEL2 0
// CKSEL1 0
// CKSEL0 1
// SUT1 1
// SUT0 0
//
// These will use the 1MHz internal oscillator as clock source, if you just plug in a raw
// ATMega8, these will be the settings and FOSC in the Makefile should be 1000000 (1MHz)
void init();
void setupInput();
void mapInput2Relay();
void sendbyte( uint8_t databyte );
uint8_t getbyte();
void sendStr( char *str );
void processCommand( uint8_t relaynum, uint8_t state );
int main(void)
{
init();
setupInput();
sendStr("360VL AVR IO driver v1.0\r\n");
/* uncomment this while if you want to drive relays by screw terminal input */
while( 1 )
{
mapInput2Relay();
}
/* uncomment this while if you want to command the relays by typing serial commands to the board
see processCommand for syntax
uint8_t databyte1,databyte2;
while( 1 )
{
databyte1 = getbyte();
sendbyte( databyte1 );
databyte2 = getbyte();
sendbyte( databyte2 );
processCommand( databyte1 - 48, databyte2 - 48 );
}
*/
}
void init()
{
// Input/Output Ports initialization
// Port B
RELAYPORT = 0x00;
DDRB=0x0F; // O1-4 are on PB3-PB0 respectively
// Port C
PORTC=0x00;
DDRC=0x20;
// Port D
PORTD=0x00;
DDRD=LED; // IN1 is on port D but is input, so nothing to do on the DDRD for it
// LED is tied to +5V opposite. avr pin side would be gnd, so take it low
LEDPORT &= ~LED;
// test - this should fire all 4 relay ports and turn their LEDs on in the process
// RELAYPORT |= 0x0f; // send them all high
// test fire 1 relay - this succeeds, no problems
// RELAYPORT |= RELAY1;
// baud rate set
UBRRH = (uint8_t)(UBBR_VAL >> 8 );
UBRRL = (uint8_t)(UBBR_VAL);
UCSRB = (1<<RXEN)|(1<<TXEN);
// to access UCSRC which shares the same i/o location as UBRRH, the URSEL bit must
// be set, otherwise it will update UBRRH instead
// URSEL - must be 1 to write to UCSRC
// UMSEL - 0 asynchronous - 1 synchronous
// USBS - 0 1 stop bit, 1, 2 stop bits
// UPM1 UPM0 - 10 - even parity, 11, odd parity, 00 disabled (no parity)
// UCSZ2 UCSZ1 UCSZ0 - 011 (8 bits), 010 (7bits)
UCSRC = (1<<URSEL)|
(0<<UMSEL)|
(0<<USBS)|
(1<<UCSZ1)|(1<<UCSZ0);
}
void sendbyte( uint8_t databyte )
{
// if byte is being transmitted, wait for that
while((UCSRA&(1<<UDRE)) == 0);
UDR = databyte;
}
uint8_t getbyte()
{
// wait until a byte is received
while( (UCSRA&(1<<RXC)) == 0);
return UDR;
}
void sendStr( char *str )
{
while ( *str != '\0' )
{
sendbyte( (uint8_t)*str++);
}
}
/* this is a simple command line processor so instead of controlling relays by screw terminal input,
you can type commands to it from the serial port to flip the relays.
The syntax is simply [relay number][0|1].
Example:
11
will turn the relay number 1 on
10
will turn the relay number 1 off
relay numbers go from 1-4
*/
void processCommand( uint8_t relaynum, uint8_t state )
{
char output[80];
sprintf(output,"relay: %02x, state: %02x\n\r", relaynum, state );
sendStr( output );
switch( relaynum )
{
case 1:if ( state >= 1 )
RELAYPORT |= RELAY1;
else
RELAYPORT &= ~RELAY1;
break;
case 2:if ( state >= 1 )
RELAYPORT |= RELAY2;
else
RELAYPORT &= ~RELAY2;
break;
case 3:if ( state >= 1 )
RELAYPORT |= RELAY3;
else
RELAYPORT &= ~RELAY3;
break;
case 4:if ( state >= 1 )
RELAYPORT |= RELAY4;
else
RELAYPORT &= ~RELAY4;
break;
default:
sprintf(output,"unrecognized relay number: %d", relaynum );
sendStr( output );
break;
}
}
void setupInput()
{
// PORT A
ADCSRA = 0x00; // disable ADC
// PORTA=(IN2 | IN3 | IN4); // if I set these states, later, PORTA will be read all high, even when input is low
PORTA=0x00; // if I set these states on PORTA, later, it will be read all low, even when the input is high
DDRA=0x00; // all input for now
}
void mapInput2Relay()
{
uint8_t in1=0, in2=0, in3=0, in4=0, porta, portd;
// a high input on the screw terminal will be a low input to the avr.
// read IN1 on portD
portd = PIND;
// mask off just the PD2 bit and turn it into a boolean, re-invert the value so it matches input terminals
in1 = (~(PIND) >> PD2) & 0x01;
// read the rest of the inputs from port A
porta = PINA;
// mask off each input and turn it into a boolean - re-invert the value so it matches input terminals
in2 = (~porta >> PA0 ) & 0x01;
in3 = (~porta >> PA1 ) & 0x01;
in4 = (~porta >> PA2 ) & 0x01;
// flip the relays - if boolean 1, turn the flip it from normal state
if ( in1 == 1 )
RELAYPORT |= RELAY1;
else
RELAYPORT &= ~RELAY1;
if ( in2 == 1 )
RELAYPORT |= RELAY2;
else
RELAYPORT &= ~RELAY2;
if ( in3 == 1 )
RELAYPORT |= RELAY3;
else
RELAYPORT &= ~RELAY3;
if ( in4 == 1 )
RELAYPORT |= RELAY4;
else
RELAYPORT &= ~RELAY4;
}