add source code

This commit is contained in:
drew 2019-10-18 22:08:59 +02:00
parent 4387f576ef
commit cd17ebe638
11 changed files with 558 additions and 1 deletions

View File

@ -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.

138
gates.c Executable file
View File

@ -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
}

33
makefile Executable file
View File

@ -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

55
mytimer.c Executable file
View File

@ -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
};

4
mytimer.h Executable file
View File

@ -0,0 +1,4 @@
//Timing related functions
void initTimer(void);
unsigned long mytime(void);
accum ssinceboot(void);

99
safety.c Executable file
View File

@ -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
};

5
safety.h Executable file
View File

@ -0,0 +1,5 @@
//light and sound related functions
void idle(void);
void light_clear(void);
void light_blocked(char sound);
void eval(void);

99
stepper.c Executable file
View File

@ -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
};

6
stepper.h Executable file
View File

@ -0,0 +1,6 @@
//Stepper motor related functions
int motor_step(int steps);
void motor_hold(void);
void motor_free(void);

109
usart.c Executable file
View File

@ -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;
};
};
}

6
usart.h Executable file
View File

@ -0,0 +1,6 @@
//USART-related functions
void initUSART(void);
unsigned char USART_receive(void);
void USART_send(unsigned char data);
void USART_putstring(char* StringPtr);