add source code
This commit is contained in:
parent
4387f576ef
commit
cd17ebe638
|
@ -1,3 +1,6 @@
|
|||
# xingcontrol
|
||||
|
||||
Firmware for ATMega mcu controlled model rail crossing.
|
||||
Firmware for a simple ATMega 328 mcu controlled model rail crossing.
|
||||
Accomodates two optical gates for triggering, buzzer and three (or six) leds for signalization and a stepper controller to close/lift the barriers.
|
||||
Info about crossing state and number of trains and carriages is sent over serial.
|
||||
Gimmicks include: non persistent stat keeping and crossing speed calculation in cm/s.
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
#define F_CPU 16000000UL
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdfix.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "usart.h"
|
||||
#include "safety.h"
|
||||
#include "stepper.h"
|
||||
#include "mytimer.h"
|
||||
/////////////////////////////////////////////////////////
|
||||
//macros
|
||||
|
||||
|
||||
//QOL macros
|
||||
#define setb(port,pin) port |= 1<<pin //set bit
|
||||
#define clrb(port,pin) port &= ~(1<<pin) //clear bit
|
||||
#define negb(port,pin) port ^= 1<<pin //negate bit
|
||||
/////////////////////////////////////////////////////////
|
||||
//Global vars
|
||||
char tmp[100]; //auxiliary variable for (mainly) storing converted strings
|
||||
volatile char iflag = -1; //interrupt fired flag
|
||||
volatile int trg0 = 0;
|
||||
volatile int trg1 = 0; //storing int trigger counts
|
||||
volatile char isBlocked = 0; //indicate state
|
||||
volatile unsigned long t;
|
||||
volatile unsigned long t2;
|
||||
unsigned long trgt = 0; //storing timestamps
|
||||
//int trains = 0;
|
||||
//int carriages = 0; //storing stats
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
//INTERRUPT VECTS
|
||||
ISR(INT0_vect){
|
||||
if(trg0 == 0) t = mytime(); //first trigger timestamp
|
||||
if(iflag != 0) iflag = 0; //set int_fired flag
|
||||
trg0++; //count triggers
|
||||
t2 = mytime(); //timestamp each trigger
|
||||
|
||||
/*
|
||||
//send debug info (comment out if not needed)
|
||||
sprintf(tmp,"trg0:%dflg:%d\r\n",trg0,iflag);
|
||||
USART_putstring(tmp);
|
||||
*/
|
||||
}
|
||||
ISR(INT1_vect){
|
||||
if(trg1 == 0) t = mytime(); //first trigger timestamp
|
||||
if(iflag != 1) iflag = 1; //set int_fired flag
|
||||
trg1++; //count triggers
|
||||
t2 = mytime(); //timestamp each trigger
|
||||
|
||||
/*
|
||||
//send debug info (comment out if not needed)
|
||||
sprintf(tmp,"trg1:%dflg:%d\r\n",trg1,iflag);
|
||||
USART_putstring(tmp);
|
||||
*/
|
||||
};
|
||||
/////////////////////////////////////////////////////////
|
||||
//FUNCTIONS
|
||||
void initIO(void) {
|
||||
//External interrupts init
|
||||
EICRA=(1<<ISC11) | (1<<ISC10) | (1<<ISC01) | (1<<ISC00); //enable INT0 and INT1, mode: rising edge
|
||||
EIMSK=(1<<INT1) | (1<<INT0); //enable interrupt vectors
|
||||
EIFR=(1<<INTF1) | (1<<INTF0); //reset interrupt flags
|
||||
|
||||
//set io pin directions
|
||||
//PORTC 0-3 OUT -- signalling LEDs and sound
|
||||
DDRC=0xF;
|
||||
//PORTB 1-5 OUT -- status LED and stepper controller
|
||||
DDRB=0x3E;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
//executed once after every reset
|
||||
sei(); //globally enable interrupts
|
||||
initIO(); //initialize IO pors
|
||||
initTimer(); //initialize timers
|
||||
initUSART(); //intialize USART
|
||||
//init_ok flash with all lights and buzzer
|
||||
PORTC = 0xF;
|
||||
_delay_ms(200);
|
||||
PORTC = 0x00;
|
||||
USART_putstring("Init_OK\r\nSTATS RESET!\r\n"); //send info about successful init
|
||||
|
||||
//local vars
|
||||
char iflag_prev = -1;
|
||||
|
||||
//infinite loop
|
||||
while (1) {
|
||||
switch(iflag){
|
||||
case 0:
|
||||
if(isBlocked == 0){
|
||||
isBlocked = 1; //change state to blocked
|
||||
iflag_prev = 0; //note which flag was set
|
||||
motor_step(50); //lower gate
|
||||
USART_putstring("Train passing from right...\r\n"); //send info
|
||||
trgt = t; //timestamp trigger time
|
||||
}
|
||||
if(isBlocked == 1 && iflag_prev == 1 && trg0 == trg1){
|
||||
isBlocked = 0; //change state
|
||||
motor_step(-50); //raise gate
|
||||
eval(); //evaluate stats
|
||||
iflag = -1; //clear int fired flag
|
||||
trg0 = 0;
|
||||
trg1 = 0; //clear trigger counts
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if(isBlocked == 0){
|
||||
isBlocked = 1;
|
||||
iflag_prev = 1;
|
||||
motor_step(50);
|
||||
USART_putstring("Train passing from left...\r\n");
|
||||
trgt = t;
|
||||
}
|
||||
if(isBlocked == 1 && iflag_prev == 0 && trg1 == trg0){
|
||||
isBlocked = 0;
|
||||
motor_step(-50);
|
||||
eval();
|
||||
iflag = -1;
|
||||
trg0 = 0;
|
||||
trg1 = 0;
|
||||
}
|
||||
break;
|
||||
};
|
||||
|
||||
if(isBlocked == 1){
|
||||
light_blocked(1); //lights blinking red with(1)/without(0) sound
|
||||
}
|
||||
else {
|
||||
light_clear(); //lights blinking white, no sound
|
||||
};
|
||||
}
|
||||
return 0; //hopefully never reached
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
MCU = atmega328p
|
||||
PROG = usbasp
|
||||
FREQ = 16000000UL
|
||||
CC = avr-gcc
|
||||
CFLAGS = -mmcu=$(MCU) -DF_CPU=$(FREQ) -Wl,-u,vfprintf -lprintf_flt -lm -I. -Os -mcall-prologues -Wall
|
||||
OBJCOPY = avr-objcopy
|
||||
OFLAGS = -j .text -j .data -O ihex
|
||||
FLASHER = avrdude
|
||||
FFLAGS = -p $(MCU) -c $(PROG) -U flash:w:
|
||||
|
||||
TARGET = gates
|
||||
EXTS = usart.c safety.c stepper.c mytimer.c
|
||||
DEPS = usart.o safety.o stepper.o mytimer.o
|
||||
|
||||
all: compile convert upload
|
||||
bin: compile
|
||||
build: compile convert
|
||||
flash: upload
|
||||
convert: compile
|
||||
|
||||
compile: $(TARGET).c
|
||||
$(CC) $(CFLAGS) -c $(EXTS)
|
||||
$(CC) $(CFLAGS) -o $(TARGET).out $(DEPS) $(TARGET).c
|
||||
convert:
|
||||
$(OBJCOPY) $(OFLAGS) $(TARGET).out $(TARGET).hex
|
||||
rm -f *.out
|
||||
upload:
|
||||
$(FLASHER) $(FFLAGS)$(TARGET).hex
|
||||
rm -f *.hex
|
||||
clean:
|
||||
rm -f $(TARGET).out *.hex
|
||||
clear:
|
||||
rm -f *.o *.out *.hex
|
|
@ -0,0 +1,55 @@
|
|||
#define F_CPU 16000000UL
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdfix.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "mytimer.h"
|
||||
|
||||
volatile unsigned long ovf_cnt = 0;
|
||||
|
||||
void initTimer(void){
|
||||
//Timer/Counter 0 init
|
||||
TCNT0 = 0; //timer0 init with value 0
|
||||
TIMSK0 |= (1<<TOIE0); //enable ovf interrupt
|
||||
TCCR0B |= (1<<CS01)|(1<<CS00); //start timer0 with clk/64 prescaler
|
||||
/*
|
||||
//Timer/Counter 1 init
|
||||
TCNT1 = 0; //initialize timer with value 0
|
||||
TCCR1B |= (1<<ICES1); //input capture on rising edge
|
||||
TIMSK1 |= (1<<ICIE1)|(1<<TOIE1); //enable input capture and ovf interrupts
|
||||
TCCR1B |= (1<<CS12)|(1<<CS10); //start timer with clk/1024 prescaler
|
||||
*/
|
||||
};
|
||||
|
||||
//Timer0 overflow interrupt vector
|
||||
ISR(TIMER0_OVF_vect){
|
||||
ovf_cnt++; //increment number of ovfs
|
||||
};
|
||||
|
||||
/*Timer1 currently unused
|
||||
//Timer1 input capture vector
|
||||
ISR(TIMER1_CAPT_vect){
|
||||
|
||||
};
|
||||
//Timer1 ovf vector
|
||||
ISR(TIMER1_OVF_vect){
|
||||
|
||||
};
|
||||
*/
|
||||
|
||||
//returns microseconds elapsed from reset -- overflows after cca 70hrs
|
||||
//ovf_cnt * 256 -- amount of ticks between ovfs
|
||||
//+TCNT0 -- add actual timer value
|
||||
//*4 -- step is ~4us/tick due to prescaling -- multiplication to get correct value in us from ticks
|
||||
//!!!NOT GENERALIZED, WORKS ONLY WITH 16MHz SYSTEM CLOCK!!!
|
||||
unsigned long mytime(){
|
||||
return((ovf_cnt * 256) + TCNT0) * 4;
|
||||
};
|
||||
|
||||
accum ssinceboot(){
|
||||
return mytime() * 1e-6; //return current timestamp already in seconds
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
//Timing related functions
|
||||
void initTimer(void);
|
||||
unsigned long mytime(void);
|
||||
accum ssinceboot(void);
|
|
@ -0,0 +1,99 @@
|
|||
#define F_CPU 16000000UL
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdfix.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "mytimer.h"
|
||||
#include "usart.h"
|
||||
///////////////////////////////////////////////////////////
|
||||
//MACROS
|
||||
#define GATE_DISTcm 20 //distance between gates in cm
|
||||
|
||||
#define setb(port,pin) port |= 1<<pin //set bit
|
||||
#define clrb(port,pin) port &= ~(1<<pin) //clear bit
|
||||
#define negb(port,pin) port ^= 1<<pin //negate bit
|
||||
///////////////////////////////////////////////////////////
|
||||
//global vars
|
||||
char tmp[100];
|
||||
unsigned long t;
|
||||
unsigned long t2;
|
||||
int trains = 0;
|
||||
int carriages = 0; //storing stats
|
||||
int trg0;
|
||||
int trg1;
|
||||
unsigned long trgt;
|
||||
|
||||
void idle(void){
|
||||
setb(PORTB, 5);
|
||||
_delay_ms(100);
|
||||
clrb(PORTB, 5);
|
||||
_delay_ms(100);
|
||||
setb(PORTB, 5);
|
||||
_delay_ms(100);
|
||||
clrb(PORTB, 5);
|
||||
_delay_ms(1000);
|
||||
};
|
||||
void light_clear(void){
|
||||
if(bit_is_set(PORTC,0) || bit_is_set(PORTC,2)){
|
||||
clrb(PORTC,0);
|
||||
clrb(PORTC,2);
|
||||
};
|
||||
negb(PORTC,1);
|
||||
_delay_ms(700);
|
||||
};
|
||||
void light_blocked(char sound){
|
||||
if(bit_is_set(PORTC,1)) clrb(PORTC,1);
|
||||
//mute sound signalling
|
||||
if(sound == 0){
|
||||
setb(PORTC,0);
|
||||
_delay_ms(500);
|
||||
clrb(PORTC,0);
|
||||
setb(PORTC,2);
|
||||
_delay_ms(500);
|
||||
clrb(PORTC,2);
|
||||
}
|
||||
else { //enable sound signalling
|
||||
setb(PORTC,0);
|
||||
for(int i = 0;i < 110;i++){ //sound generation
|
||||
setb(PORTC,3);
|
||||
_delay_us(900);
|
||||
clrb(PORTC,3);
|
||||
_delay_us(900);
|
||||
};
|
||||
_delay_ms(75);
|
||||
for(int i = 0;i < 116;i++){ //sound generation again
|
||||
setb(PORTC,3);
|
||||
_delay_us(870);
|
||||
clrb(PORTC,3);
|
||||
_delay_us(870);
|
||||
};
|
||||
clrb(PORTC,0);
|
||||
setb(PORTC,2);
|
||||
|
||||
/*alternate warning sound
|
||||
for(int i = 0;i < 160;i++){
|
||||
setb(PORTC,3);
|
||||
_delay_us(600);
|
||||
clrb(PORTC,3);
|
||||
_delay_us(600);
|
||||
};
|
||||
*/
|
||||
_delay_ms(500); //if using alternate sound adjust delay accordingly
|
||||
clrb(PORTC,2);
|
||||
};
|
||||
};
|
||||
void eval(void){
|
||||
accum trvt = 0;
|
||||
accum spd = 0; //fixed point decimal vars
|
||||
|
||||
trvt = (t2 - trgt) * 1e-6; //calculate time spent crossing in s
|
||||
spd = GATE_DISTcm / ((t - trgt) * 1e-6); //calculate speed in cm/s (gate distance can be adjusted by editing macro at the top)
|
||||
sprintf(tmp,"Train passed: %d carriage(s), time %.2fs, speed %.2fcm/s\r\n",trg0/2,(double)trvt,(double)spd); //convert stats to format readable by USART_putstring i.e. a string
|
||||
USART_putstring(tmp); //send info
|
||||
trains++; //increment amount of trains passed
|
||||
carriages = carriages + trg0/2; //increment amount of carriages
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
//light and sound related functions
|
||||
void idle(void);
|
||||
void light_clear(void);
|
||||
void light_blocked(char sound);
|
||||
void eval(void);
|
|
@ -0,0 +1,99 @@
|
|||
//stepper motor functions
|
||||
#define F_CPU 16000000UL
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "usart.h"
|
||||
|
||||
// QOL macros
|
||||
#define setb(port,pin) port |= 1<<pin //set bit
|
||||
#define clrb(port,pin) port &= ~(1<<pin) //clear bit
|
||||
#define negb(port,pin) port ^= 1<<pin //negate bit
|
||||
|
||||
int steps;
|
||||
int current_step;
|
||||
int i;
|
||||
char tmp[100];
|
||||
|
||||
void motor_hold(void){
|
||||
PORTB = 0b00001010; //energize opposite windings to prevent movement.
|
||||
};
|
||||
|
||||
void motor_free(void){
|
||||
PORTB = 0x00; //do not power any winding making the rotor free to rotate (no holding torque!)
|
||||
};
|
||||
|
||||
int motor_step(int steps){
|
||||
if(steps > 0){
|
||||
//move clockwise
|
||||
for(i = 0;i <= steps;i++){
|
||||
current_step = i % 4; //perform required part of rotation -- provides full resolution of 1.8deg per step
|
||||
|
||||
/*
|
||||
//debug info
|
||||
sprintf(tmp,"current_step %d\r\n",current_step);
|
||||
USART_putstring(tmp);
|
||||
*/
|
||||
|
||||
//energize windings in required order -- delay controls speed
|
||||
switch(current_step){
|
||||
case 0:
|
||||
PORTB = 0b00001010;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
case 1:
|
||||
PORTB = 0b00001100;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
case 2:
|
||||
PORTB = 0b00010100;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
case 3:
|
||||
PORTB = 0b00010010;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
||||
if(steps < 0){
|
||||
//step counterclockwise when negative value is entered
|
||||
for(i = steps;i <= 0;i++){
|
||||
//mod is negative when calculated from negative number for some reason, this takes care of that
|
||||
current_step = (i % 4) * -1;
|
||||
|
||||
/*
|
||||
//debug info
|
||||
sprintf(tmp,"current_step %d\r\n",current_step);
|
||||
USART_putstring(tmp);
|
||||
*/
|
||||
|
||||
switch(current_step){
|
||||
case 0:
|
||||
PORTB = 0b00001010;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
case 1:
|
||||
PORTB = 0b00001100;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
case 2:
|
||||
PORTB = 0b00010100;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
case 3:
|
||||
PORTB = 0b00010010;
|
||||
_delay_ms(5);
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
||||
else {
|
||||
motor_hold();
|
||||
};
|
||||
return i; //return number of steps made -- not in use atm
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
//Stepper motor related functions
|
||||
int motor_step(int steps);
|
||||
void motor_hold(void);
|
||||
void motor_free(void);
|
||||
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
#define F_CPU 16000000UL
|
||||
|
||||
#include <avr/io.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdfix.h>
|
||||
#include <util/delay.h>
|
||||
#include <avr/interrupt.h>
|
||||
#include "usart.h"
|
||||
#include "mytimer.h"
|
||||
|
||||
//global variables
|
||||
char rx[21]; //USART input buffer
|
||||
char tmp[100]; //auxiliary var for converting strings
|
||||
volatile unsigned int cnt = 0; //counter
|
||||
volatile char rxCmd; //input is command flag
|
||||
int trains;
|
||||
int carriages; //storing stats
|
||||
int trg0;
|
||||
int trg1;
|
||||
volatile char iflag;
|
||||
volatile char isBlocked; //state information
|
||||
accum tsb; //store time since boot
|
||||
|
||||
|
||||
//USART baudrate calculations
|
||||
#define BAUDRATE 9600
|
||||
#define BAUD_PRESCALER (((F_CPU / (BAUDRATE * 16UL))) - 1)
|
||||
|
||||
|
||||
//USART-related function definitions
|
||||
void initUSART(void){
|
||||
UBRR0H = (uint8_t)(BAUD_PRESCALER>>8);
|
||||
UBRR0L = (uint8_t)(BAUD_PRESCALER); //setting baudrate to high and low regs
|
||||
// UCSR0B = (1<<RXEN0)|(1<<TXEN0); //rx tx with no interrupts
|
||||
UCSR0B = 0x98; //enable RX interrupt
|
||||
UCSR0C = (3<<UCSZ00); //data bit size
|
||||
}
|
||||
unsigned char USART_receive(void){
|
||||
while(!(UCSR0A & (1<<RXC0))); //wait for empty transmit buffer
|
||||
return UDR0; //return buffer value
|
||||
}
|
||||
void USART_send(unsigned char data){
|
||||
while(!(UCSR0A & (1<<UDRE0))); //while buffer reg is empty
|
||||
UDR0 = data; //put data into buffer reg
|
||||
}
|
||||
void USART_putstring(char* StringPtr){
|
||||
while(*StringPtr != 0x00){
|
||||
USART_send(*StringPtr);
|
||||
StringPtr++;}
|
||||
}
|
||||
|
||||
//USART recieve interrupt
|
||||
ISR(USART_RX_vect) {
|
||||
rx[cnt] = UDR0; //store incoming characters in buffer
|
||||
cnt++; //increment count of characters
|
||||
if (cnt == 20 || rx[cnt-1] == 13){ //process data if data exceeds 20 characters or carriage return is entered
|
||||
if(rx[0] == 58) rxCmd=1; //if a character sequence starts with : it is a command
|
||||
else rxCmd=0;
|
||||
rx[cnt] = 0; //add zero at the end to produce a string literal
|
||||
switch (rxCmd){
|
||||
case 0:
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\n"); //echo entered characters to console
|
||||
cnt=0;
|
||||
break;
|
||||
case 1:
|
||||
if(strcmp(":hello\r",rx) == 0){ //find what command is entered and respond accordingly
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\nWELCOME\r\nNOTE: Stats are not saved between reboots\r\n");
|
||||
}
|
||||
else if(strcmp(":help\r",rx) == 0){
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\nAvailable commands:\r\n:hello -- Show system status\r\n:stats -- Show system stats\r\n:block -- Manually set barriers down\r\n:ublock -- Manually clear blockage\r\n:help -- Display this help\r\n");
|
||||
}
|
||||
else if(strcmp(":stats\r",rx) == 0){
|
||||
tsb = ssinceboot() / 60;
|
||||
USART_putstring(rx);
|
||||
sprintf(tmp,"\r\nIn the %.2fmin since boot %d trains passed totalling %d carriages.\r\n",(double)tsb,trains,carriages);
|
||||
USART_putstring(tmp);
|
||||
}
|
||||
else if(strcmp(":block\r",rx) == 0){
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\nManual override: crossing is blocked\r\n");
|
||||
isBlocked = 1;
|
||||
}
|
||||
else if(strcmp(":unblock\r",rx) == 0){
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\nManual override: crossing is clear\r\n");
|
||||
isBlocked = 0;
|
||||
iflag = -1;
|
||||
trg0 = trg1 = 0; //set state to clear, reset int_fired flag and trigger counts
|
||||
}
|
||||
else {
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\n Unknown Command\r\n");
|
||||
}
|
||||
rxCmd=0;
|
||||
cnt=0;
|
||||
break;
|
||||
default:
|
||||
USART_putstring(rx);
|
||||
USART_putstring("\r\n");
|
||||
cnt=0; //echo entered characters and reset the buffer by default
|
||||
break;
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue