#define debug
//MCU ATMega16, 14.7456MHz
/*PIN assignment
LCD display (standard text controller):
PC0-3=DB4-7
PC4=E
PC5=RW
PC6=RS
PC7= reserved (for light via NPN transistor - 150mA)
Rotational coder (capacitors 100nF at contacts necessary to prevent spurious edges):
PD2 (left)
PD3 (right)
PB2 (press)
FETs:
PD4 charge
PD5 weld_power
PD6 discharge
ADCs:
PA0,1,2 .. potenciometr 0,1,2
PA3 ... Vext R divisor 33k+220k
PA4 ... Vcap R divisor 33k+220k
LED: PB0 via 470R
Triger pedal switch (normally open): PB1 (using internal pullup, 100nF cap. parallel)
*/
#define XTAL 14745600
#define BAUD 19200
#include "backward.h"
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <setjmp.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <stdlib.h>
#include <string.h>
#if defined(at90s2313) || defined(at90s8535)
#else
#define ATmega
#endif
#ifdef ATmega
#define USR UCSRA
#endif
#ifndef MCU
#define emulate
#else
#define AVR_assembler
#endif
#define itoa10(N,S) itoa(N,S,10)
#define itoa16(N,S) itoa(N,S,16)
void printP (PGM_P string){
char c;
c=pgm_read_byte(string);
while (c) {
loop_until_bit_is_set(USR, UDRE);
UDR = c;
c=pgm_read_byte(++string);
}
return;
}
void print (char *string){
while (*string) {
loop_until_bit_is_set(USR, UDRE);
UDR = *string++;
}
return;
}
//UART initialize
#ifdef ATmega
#define UCR UCSRB
#define UART_INIT(baud) { \
UBRRH=0; \
UBRRL= (XTAL/baud+15)/16-1; \
UCSRB=(1<<TXEN)|(1<<RXEN)|(1<<RXCIE); \
UCSRC=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1)|(1<<USBS); }
#else
#define UART_INIT(baud) { \
UBRR = (XTAL/baud+15)/16-1; \
sbi(UCR, TXEN); \
sbi(UCR, RXEN); \
}
#endif
//UART routines
volatile uint8_t inuartlen=0;
volatile uint8_t uartline=0;
#define MAXUART 64
volatile char inuart[MAXUART];
INTERRUPT(SIG_UART_RECV)
{
char c=UDR;
if(uartline) return; //ignore until line is processed
UDR = c; //echo
if(c=='\n' || c== '\r' || inuartlen == MAXUART-1) {uartline=1; inuart[inuartlen]=0; inuartlen=0;}
else inuart[inuartlen++]=c;
}
uint16_t Vset = 120;
watchdog(uint8_t onoff)
{
if(onoff) {wdt_enable(WDTO_2S); wdt_reset();}
else {wdt_reset();wdt_disable();}
}
//rotational coder routines
volatile static uint8_t menustate=0;
volatile static int8_t menuselect=0;
#define N_MENULABEL 1
typedef char MENULABEL[9];
static const MENULABEL menulabel[N_MENULABEL] = {"WELDER"};
void process_rotation(uint8_t sense)
{
switch (menustate) {
case 1:
if(sense) ++menuselect; else --menuselect;
if(menuselect<0) menuselect+=N_MENULABEL;
menuselect = menuselect%N_MENULABEL;
break;
case 0:
if(sense) ++Vset; else --Vset;
if(Vset<10) Vset=10;
if(Vset>350) Vset=350;
default:
break;
}
}
//right:
//I0:01
//I1:00
//I0:10
//I1:11
//
//left:
//I1:10
//I0:00
//I1:01
//I0:11
//
volatile static int8_t rotcount=0;
void rotcoder(uint8_t interrupt, uint8_t pins)
{
if(interrupt)
{
if(pins==0 || pins==3) ++rotcount;
else --rotcount;
}
else
{
if(pins==0 || pins==3) --rotcount;
else ++rotcount;
}
if(rotcount==4) {process_rotation(1); rotcount=0;}
if(rotcount== -4) {process_rotation(0); rotcount=0;}
}
volatile static uint8_t light=0;
void process_press(uint8_t length)
{
switch (menustate) {
default:
if(length) //long press
{
}
else //short press
{
}
}
}
INTERRUPT(SIG_INTERRUPT0)
{
rotcoder(0,(PIND>>2)&3);
}
INTERRUPT(SIG_INTERRUPT1)
{
rotcoder(1,(PIND>>2)&3);
}
volatile static uint8_t timer0x=0;
INTERRUPT(SIG_INTERRUPT2)
{
uint16_t tim;
uint8_t x;
cbi(GIMSK,INT2);
cbi(GICR,INT2);
//toggle the active edge
if(x=bit_is_set(PINB,PB2))
{
cbi(MCUCSR,ISC2);
tim=timer0x;
tim = (tim<<8)|TCNT0;
}
else
{
sbi(MCUCSR,ISC2);
TCNT0=0;
timer0x=0;
}
cbi(GIFR,INTF2);
sbi(GICR,INT2);
sbi(GIMSK,INT2);
if(!x) return;
process_press(tim>3000);
}
INTERRUPT(SIG_OVERFLOW0)
{
++timer0x;
}
init_rotcoder(void)
{
//start timer0
TCNT0=0;
TCCR0=CK1024;
OCR0=0;
sbi(TIMSK,TOIE0);
//pull-up pins
cbi(DDRD,PD2); sbi(PORTD,PD2);
cbi(DDRD,PD3); sbi(PORTD,PD3);
cbi(DDRB,PB2); sbi(PORTB,PB2);
//and allow edge interrupts on INT0,1,2
sbi(MCUCR,ISC00); cbi(MCUCR,ISC01);
sbi(GIMSK,INT0);
sbi(MCUCR,ISC10); cbi(MCUCR,ISC11);
sbi(GIMSK,INT1);
cbi(MCUCSR,ISC2);
sbi(GIMSK,INT2);
}
//delay routines
void delay_xs(uint16_t xs) //xs takes 4 clocks time
{
asm volatile (
"\n"
"L_dl1%=:" "\n\t"
"sbiw %0, 1" "\n\t"
"brne L_dl1%=" "\n\t"
: "=&w" (xs)
);
return;
}
void delay_ms(uint16_t ms) //ms takes 1/1000 s
{
while(ms--) delay_xs(XTAL/4/1000);
}
void delay_ms10(uint8_t ms) //ms takes 1/10000 s
{
while(ms--) delay_xs(XTAL/4/10000);
}
void delay_ms100(uint8_t ms) //ms takes 1/100000 s
{
while(ms--) delay_xs(XTAL/4/100000);
}
//display routines
#define lcd_delay 50
void lcd_w4bit(uint8_t rs, uint8_t x)
{
PORTC= 1<<PC4|light
#if (MCU == atmega8 )
; if(rs) sbi(PORTD,PD4); else cbi(PORTD,PD4);
#else
| rs <<PC6;
#endif
delay_xs(lcd_delay);
PORTC |= x&0x0f;
delay_xs(lcd_delay);
cbi(PORTC,PC4);
delay_xs(lcd_delay);
sbi(PORTC,PC4);
delay_xs(lcd_delay);
}
uint8_t lcd_r4bit(uint8_t rs)
{
uint8_t r;
PORTC= 1<<PC4 | 1<<PC5|light
#if (MCU == atmega8 )
; if(rs) sbi(PORTD,PD4); else cbi(PORTD,PD4);
#else
| rs <<PC6;
#endif
delay_xs(lcd_delay);
cbi(PORTC,PC4);
delay_xs(lcd_delay);
r=PINC&0x0f;
cbi(PORTC,PC5);
delay_xs(lcd_delay);
return r;
}
uint8_t lcd_rbyte(uint8_t rs)
{
#if (MCU == atmega8 )
sbi(DDRD,PD4);
DDRC=0x30;
#else
DDRC=0xf0;
#endif
delay_xs(lcd_delay);
return (lcd_r4bit(rs)<<4)|lcd_r4bit(rs);
#if (MCU == atmega8 )
sbi(DDRD,PD4);
DDRC=0x3f;
#else
DDRC=0xff;
#endif
delay_xs(lcd_delay);
}
void lcd_init4bit(void)
{
#if (MCU == atmega8 )
sbi(DDRD,PD4);
DDRC=0x3f;
#else
DDRC=0xff;
#endif
delay_xs(20000);
lcd_w4bit(0,3);
delay_xs(10000);
lcd_w4bit(0,3);
delay_xs(500);
lcd_w4bit(0,3);
lcd_w4bit(0,2);
}
void lcd_wbyte(uint8_t rs, uint8_t x)
{
lcd_w4bit(rs,x>>4);
lcd_w4bit(rs,x);
}
void lcd_clear(void)
{
lcd_wbyte(0,0x01);
delay_xs(30000);
}
void lcd_init(void)
{
lcd_init4bit();
lcd_wbyte(0,0x28);
lcd_wbyte(0,0x08);
lcd_clear();
lcd_wbyte(0,0x06);
lcd_wbyte(0,0x0c);
}
void lcd_print(char *t)
{
while(*t) if(*t=='\n') {++t; lcd_wbyte(0,0xc0);} else lcd_wbyte(1,*t++);
}
void lcd_print8(char *t)
{
uint8_t l=0;
while(*t) {lcd_wbyte(1,*t++); ++l;}
while(l<8) {lcd_wbyte(1,' '); ++l;}
}
void lcd_cursor(uint8_t r, uint8_t c)
{
lcd_wbyte(0,0x80+c+(r<<6));
}
void printx(uint16_t x, char z, char *t)
{
strcpy(t," . ");
t[4]= z;
t[3]= '0'+x%10; x/=10;
t[1]= '0'+x%10; x/=10;
t[0]= '0'+x%10;
}
void printy(uint16_t x, char z, char *t)
{
strcpy(t," . ");
t[4]= z;
t[3]= '0'+x%10; x/=10;
t[2]= '0'+x%10; x/=10;
t[0]= '0'+x%10;
}
uint16_t adcread(uint8_t i)
{
ADMUX= i;
ADCSRA= (1<<ADEN)|(1<<ADIF)|(1<<ADSC)|7;
loop_until_bit_is_set(ADCSRA,ADIF);
uint16_t v=ADCL;
v|= (ADCH<<8);
return v;
}
uint16_t voltage(uint8_t i)
{
uint32_t tmp= 1176; //calibrated for a given voltage divider resistor tolerance and the fact that the reference voltage is not exactly 5V but depends on the particular stabilizer. High accuracy is not needed anyway
tmp *= adcread(i);
tmp /= 3;
return tmp>>10;
}
int main(void)
{
uint16_t pass;
UART_INIT(BAUD);
//global interrupt activate
sbi(SREG,7);
//setup inputs and outputs
sbi(DDRB,PB0); cbi(PORTB,PB0); //led
cbi(DDRB,PB1); sbi(PORTB,PB1); //trigger
sbi(DDRD,PD4); cbi(PORTD,PD4); //charge
sbi(DDRD,PD5); cbi(PORTD,PD5); //weld
sbi(DDRD,PD6); cbi(PORTD,PD6); //discharge
//write initial message to the display and blink leds
lcd_init();
lcd_print("Spot Welder");
printP(PSTR("Spot Welder Reset!\n"));
delay_ms(500);
lcd_clear();
//enable rotcoder
init_rotcoder();
uint16_t Vext, Vcap;
uint8_t times[3];
const uint8_t timemax[3]={200,150,150};
//MAIN LOOP
while(1)
{
++pass;
uint8_t i;
//read potentiometers
for(i=0; i<=2; ++i)
{
uint32_t tmp= timemax[i];
tmp *= (adcread(i)+1);
times[i]= tmp>>10;
}
//read Vext and Vcap
Vext = voltage(3);
Vcap = voltage(4);
//charge/discharge for a while if needed
cbi(PORTD,PD5);
cbi(PORTD,PD4);
cbi(PORTD,PD6);
if(Vcap<Vset) sbi(PORTD,PD4); //charge
if(Vcap>Vset+1) sbi(PORTD,PD6); //discharge
//
//control display according state set by asynchronous events
switch (menustate) {
case 1: //in menu
break;
case 0:
default:
{
char text[17];
printx(Vext,' ',text);
printx(Vset,' ',text+5);
printx(Vcap,' ',text+10);
lcd_cursor(0,0);
lcd_print(text);
printy(times[0],' ',text);
printx(times[1],' ',text+5);
printx(times[2],' ',text+10);
lcd_cursor(1,0);
lcd_print(text);
}
break;
}
//if ready, light the LED and check trigger
if(abs(Vset-Vcap)<2)
{
sbi(PORTB,PB0);
if(bit_is_clear(PINB,PB1))
{
uint16_t Vcap0 = Vcap;
printP(PSTR("FIRE!\n"));
if(times[0]) {sbi(PORTD,PD5); delay_ms100(times[0]);}
cbi(PORTD,PD5); delay_ms10(times[1]);
uint16_t Vcap1 = voltage(4);
if(times[2]) {sbi(PORTD,PD5); delay_ms10(times[2]);}
cbi(PORTD,PD5);
delay_ms(1);
Vcap = voltage(4);
char text[6];
printx(Vcap,' ',text);
lcd_clear();
lcd_cursor(0,0);
lcd_print("Vcap left ");
lcd_cursor(0,10);
lcd_print(text);
printP(PSTR("Vcap remaining: ")); print(text); print("\n");
printP(PSTR("Pulse energies: "));
lcd_cursor(1,0); lcd_print("Puls E ");
uint32_t energy = Vcap0*Vcap0 - Vcap1*Vcap1;
energy *=94; energy /= 10000; //for 0.94Farad capacitance
itoa10((uint16_t)(energy&0xffff),text); print(text);
print(" ");
lcd_cursor(1,7); lcd_print(text);
energy = Vcap1*Vcap1 - Vcap*Vcap;
energy *=94; energy /= 10000; //for 0.94Farad capacitance
itoa10((uint16_t)(energy&0xffff),text); print(text);
print("\n");
lcd_cursor(1,11); lcd_print(text);
do{} while(bit_is_clear(PINB,PB1)); //wait for release of trigger
lcd_clear();
}
}
else
{
cbi(PORTB,PB0);
cbi(PORTD,PD5);
}
delay_ms(5);
}//while
}