Code:
/* vim: set sw=8 ts=8 si : */
/*************************************************************************
Title: linuxfocus frequency counter
Author: guido socher <guido[at]linuxfocus.org>
Copyright: GPL
**************************************************************************/
#include <io.h>
#include <progmem.h>
#include <interrupt.h>
#include <string-avr.h>
#include <stdlib.h>
#include <sig-avr.h>
#include "lcd.h" /* these are the prototypes and defines for lcd.c */
#include "avr-util.h"
#include "uart.h"
/* output line to scan for button up/down */
#define BUTTONDOWN PD5
#define BUTTONDOWNDDR DDRD
#define BUTTONDOWNPORT PORTD
#define BUTTONUP PD6
#define BUTTONUPDDR DDRD
#define BUTTONUPPORT PORTD
/* Input Push Buttons */
#define SBBUTTON PIND7
#define SBBUTTONDDR DDRD
#define SBBUTTONPIN PIND
#define SBBUTTONPORT PORTD
#define I_BUTTON PINC4
#define I_BUTTONDDR DDRC
#define I_BUTTONPIN PINC
#define I_BUTTONPORT PORTC
#define U_BUTTON PINB3
#define U_BUTTONDDR DDRB
#define U_BUTTONPIN PINB
#define U_BUTTONPORT PORTB
#define UUBUTTON PINB0
#define UUBUTTONDDR DDRB
#define UUBUTTONPIN PINB
#define UUBUTTONPORT PORTB
/* current limit input and led */
#define ILIMIT PIND3
#define ILIMITDDR DDRD
#define ILIMITPIN PIND
#define ILED PC5
#define ILEDDDR DDRC
#define ILEDPORT PORTC
/* PWMOUT0= current control, software pwm */
#define PWMOUT0 PB2
#define PWMOUT0DDR DDRB
#define PWMOUT0PORT PORTB
/* PWMOUT1= voltage control, internal hardware pwm */
#define PWMOUT1 PB1
#define PWMOUT1DDR DDRB
#define PWMOUT1PORT PORTB
static char uart_outbuf[UART_RX_BUFFER_SIZE+1];
/* max 2.2A output, can be up to 3A (3000),
* change, depends on the transformer you have */
#define MAX_I 2200
/* minial stepping for I, set to 50 for MAX_I=3000
* otherwise set to 25, change this dependent on hardware */
#define IMINSTEP 25
static unsigned char ipwm_h; /* time that PWM for I is high */
static unsigned char ipwm_phase; /* flag indicating whether we are now producing a 1 or a zero */
static int ival; /* 16 bit, the current in mA */
/* max 16V output, change dependent on the transformer you have */
#define MAX_U 160
static int uval; /* 16 bit, the Voltage in 0.1 V units, eg. 50=5V */
static unsigned char mode; /* 0=normal operation, 1=I-limit, 2=standby */
static unsigned char oldmode; /* 0=normal operation, 1=I-limit, 2=standby */
static unsigned char tmpval[3]; /* 24 bit, needed for exact math in set_u */
static unsigned char tmpval2[3]; /* 24 bit, needed for exact math in set_u */
#define reply_ok() uart_sendstr_P("ok\n")
#define reply_err() uart_sendstr_P("err\n")
/* there is only support for 16bit math operations in libc.
* to get more accurate math we have to write our own functions */
/* *x is 24 bit, result is in x (x=in/out value), y *256+256 must be < 32768*/
unsigned char divXbyY(unsigned char *x,unsigned char y){
unsigned char loop,rest;
unsigned int tmp; // 16 bit
loop=3;
/* start with highest byte */
rest=0;
while(loop>0){
tmp=rest * 256 + x[loop-1];
x[loop-1]=tmp / y;
rest=tmp % y;
loop--;
}
return(rest);
}
/* *x is 24 bit, result is in x (x=in/out value), the bigger
* number must go into x, 8bit of x times y must never go over 32768 */
void multiXbyY(unsigned char *x,int y){
unsigned char loop;
unsigned int tmp,over; // 16 bit
loop=0;
over=0;
while(loop<3){
tmp= x[loop] * y + over;
over=tmp/256;
x[loop]=tmp - (over*256);
loop++;
}
}
/* convert a 2 time 8 bit interger (16 bit=i[0] and i[1], i[2] is set to zero,
* i is 24bit but only 16 bit are used) to decimal ascii and
* print it using the printfun. Padd all the digits to 4 (but we can handle full
* 16 bit if needed)
* If addcomma is set then the number will be printed by 1/10. That is
* 35 will be " 3.5" and 3 will be " 0.3" */
/* note: this function modifies *i !! */
void longtoascii(void (*printfun)(char),unsigned char *i,unsigned char addcomma)
{
char str[7];
unsigned char pos=7;
unsigned char j=0;
i[2]=0;
do{
str[pos-1]=divXbyY(i,10);
str[pos-1]+= '0';
pos--;
j++;
/* write a "," after the last digit from the right */
if (j==1 && addcomma==1){
str[pos-1]=',';
pos--;
}
}while((i[0]|i[1]) && pos > 0);
if (str[pos] == ',' && pos > 0){
/* add the missing zero for 3 -> 0.3 */
str[pos-1]='0';
pos--;
}
/* add spaces to padd up to 4 digits */
j=pos-3;
while(j>0){
(*printfun)(' ');
j--;
}
/* now reverse the string and print */
while(pos<7){
(*printfun)(str[pos]);
pos++;
}
}
/* this function will be called in if the 8bit timer goes from ff to 00 */
SIGNAL(SIG_OVERFLOW0)
{
/* stop 8bit timer */
outp(0,TCCR0);
if (ipwm_phase==1 && ipwm_h !=0){
/* now we will produce a "1" at the output */
/* time that we should remain "1" is ipwm_h */
outp(0xFF-ipwm_h,TCNT0); /* set start value */
sbi(PWMOUT0PORT,PWMOUT0); /* 1 */
ipwm_phase=0;
}else{
/* now we will produce a "0" at the output */
/* time that we should remain "1" is ipwm_h */
outp(ipwm_h,TCNT0); /* set start value */
cbi(PWMOUT0PORT,PWMOUT0); /* 0 */
ipwm_phase=1;
}
/* start 8bit timer with 62500Hz, clock/64 */
outp(3,TCCR0);
}
/* set current limit in a non linear way, 0=up, 1=down */
unsigned char step_i(unsigned char direction)
{
if (ival<200){
/* step in 50mA (or 25) units and round to the next 50 (or 25)*/
ival=ival/IMINSTEP; /* round off values set via RS232 */
ival=ival*IMINSTEP;
if (direction){
ival-=IMINSTEP;
}else{
ival+=IMINSTEP;
}
return(0);
}
if (ival<1000){
/* step in 100mA units and round to the next 100 */
ival=ival/100; /* round off values set via RS232 */
ival=ival*100;
if (direction){
ival-=100;
}else{
ival+=100;
}
return(0);
}
/* more than 1A */
/* step in 200mA units and round to the next 200 */
ival=ival/200; /* round off values set via RS232 */
ival=ival*200;
if (direction){
ival-=200;
}else{
ival+=200;
}
return(0);
}
/* set the current limit , reads ival and writes ipwm_h */
void set_i(void)
{
int a;
if (ival>MAX_I){
ival=MAX_I;
}
if (ival<50){ /* at least 50 mA, with 8bit this is the lower limit */
ival=50;
}
if (ival<140){
/* change this to calibrate */
/* y=(a/1000) *x */
//a=65; /* use this if R38=33K */
a=94; /* use this if R38=120K */
}else if (ival<500){
/* change this to calibrate */
/* y=(a/1000) *x */
//a=75; /* use this if R38=33K */
a=107; /* use this if R38=120K */
}else{
/* change this to calibrate */
/* y=(a/1000) *x */
//a=82; /* use this if R38=33K */
a=111; /* use this if R38=120K */
}
tmpval[0]=ival & 0xff;
tmpval[1]=(ival >> 8);
tmpval[2]=0;
multiXbyY(tmpval,a);
divXbyY(tmpval,100);
divXbyY(tmpval,10);
ipwm_h=tmpval[0];
/* debug, activate to calibarate */
//longtoascii(uart_sendchar,tmpval,0);
//uart_sendchar(' ');
}
/* set the voltage , reads uval and writes OCR1 */
void set_u(void)
{
int a;
unsigned char c,b;
if (uval>MAX_U){
uval=MAX_U;
}
if (uval<0){
uval=0;
}
/* below 1V the accuracy is limited because the values are
* too much quantisized (the digital resolution gets smaller)
* If you draw a curve you find that it is 99% linear */
/* remove the programming cable if you calibrate. It influences
* the output values slightly */
if (uval<5){
/* change this to calibrate */
a=250; /* y=(a/(b*c)*x */
b=10;
c=10;
}else if (uval<25){
/* change this to calibrate */
a=305; /* y=(a/(b*c)*x */
b=10;
c=10;
/*--- end calibrate ---*/
}else if (uval<120){
/* change this to calibrate */
a=793; /* y=(a/(b*c)*x */
b=10;
c=25;
/*--- end calibrate ---*/
}else{
/* change this to calibrate */
a=637; /* y=(a/(b*c)*x */
b=20;
c=10;
/*--- end calibrate ---*/
}
/* 24bit math for better accuraty */
tmpval[0]=a & 0xff;
tmpval[1]=(a >> 8) & 0xff;
tmpval[2]=0;
multiXbyY(tmpval,uval); /* uval is smaller than tmpval */
divXbyY(tmpval,b);
divXbyY(tmpval,c);
if (mode!=2){ /* do not mess up standby state */
outp( tmpval[1],OCR1H); /* set pwm high time*/
outp( tmpval[0],OCR1L); /* set pwm high time*/
}
/* debug, activate to calibarate */
//longtoascii(uart_sendchar,tmpval,0);
//uart_sendchar(' ');
}
void toggle_standby(void){
if (mode == 2){
/* set U to zero in standby but do not modify
* the displayed value */
outp( 0,OCR1H); /* set pwm high time*/
outp( 0,OCR1L); /* set pwm high time*/
ipwm_h=0; /* current = 0 */
}
if (mode ==0){
/* activate voltage output again */
sbi(ILEDPORT,ILED); /* red led off */
set_i();
set_u();
}
}
/* update rs232*/
void updaters232(void)
{
uart_sendstr_P("u:");
tmpval[0]=uval & 0xff;
tmpval[1]=(uval >> 8);
longtoascii(uart_sendchar,tmpval,0);
if (mode==2){
uart_sendstr_P(" s:1 i:");
}else{
uart_sendstr_P(" s:0 i:");
}
tmpval[0]=ival & 0xff;
tmpval[1]=(ival >> 8);
longtoascii(uart_sendchar,tmpval,0);
if (mode==1){
uart_sendstr_P(" l:1\n");
}else{
uart_sendstr_P(" l:0\n");
}
}
/* update display */
void updatedisplay(void)
{
lcd_clrscr();
lcd_puts_P("u: ");
tmpval[0]=uval & 0xff;
tmpval[1]=(uval >> 8);
longtoascii(lcd_putc,tmpval,1);
lcd_puts_P(" V ");
if (mode==0){
lcd_puts_P("<-");
}
if (mode==2){
lcd_puts_P("standby");
}
lcd_gotoxy(0,1);
lcd_puts_P("i: ");
tmpval[0]=ival & 0xff;
tmpval[1]=(ival >> 8);
longtoascii(lcd_putc,tmpval,0);
lcd_puts_P(" mA ");
if (mode==1){
lcd_puts_P("<-");
}
}
int main(void)
{
unsigned char i,rxresult,status,j,ilimitdebounce;
unsigned int ignorebutton;
unsigned int blink; /* blink in standby mode */
char cmd;
char *val;
/* initialize display, cursor off */
lcd_init(LCD_DISP_ON);
/* current limit detection as input line */
cbi(ILIMITDDR,ILIMIT);
/* red current limit LED as output */
sbi(ILEDDDR,ILED);
sbi(ILEDPORT,ILED);
/* initialize RS232 */
uart_init();
/* initialize PWM output 0 (current control) as output */
sbi(PWMOUT0DDR,PWMOUT0);
/* setup the 8bit timer for current control */
sbi(TIMSK,TOIE0); /* enable T0 overflow0 interrupt */
outp(0,TCNT0); /* start value */
/* start 8bit timer with 62500Hz, clock/64 overflow0 every 64 */
outp(3,TCCR0);
ipwm_phase=0; /* initialize */
ival=100; /* initialize default current */
/* initialize PWM output 1 (voltage control) as output */
sbi(PWMOUT1DDR,PWMOUT1);
/* setup the 16bit timer for voltage control, in the
* 4433 this timer supports hardware pwm therefore we
* do not have to implement it in software */
outp(0x83,TCCR1A); /* enable 10bit pwm and clear on compare match */
outp(0x2,TCCR1B); /* run at 1/8 of crystal freq */
/* write high byte first */
outp(0x0,TCNT1H); /* set counter to zero*/
outp(0x0,TCNT1L); /* set counter to zero*/
uval=50; /* initialize default voltage, 50=5.0V */
mode=0;
oldmode=mode;
ignorebutton=0;
ilimitdebounce=0;
blink=0;
/* button as digital input */
cbi(I_BUTTONDDR,I_BUTTON);
cbi(U_BUTTONDDR,U_BUTTON);
cbi(UUBUTTONDDR,UUBUTTON);
cbi(SBBUTTONDDR,SBBUTTON);
/* pull up resistor on */
sbi(I_BUTTONPORT,I_BUTTON);
sbi(U_BUTTONPORT,U_BUTTON);
sbi(UUBUTTONPORT,UUBUTTON);
sbi(SBBUTTONPORT,SBBUTTON);
/* the buttons are multiplexed, enable output lines */
sbi(BUTTONDOWNDDR,BUTTONDOWN);
sbi(BUTTONUPDDR,BUTTONUP);
sei(); /* enable interrupt */
/* now we can use uart/lcd display */
set_i();
set_u();
updatedisplay();
while(1){
/* first check up (++) buttons */
sbi(BUTTONDOWNPORT,BUTTONDOWN);
cbi(BUTTONUPPORT,BUTTONUP);
j=0;
/* ignorebutton is needed for debounce */
while(j<2 && ignorebutton==0){
if (bit_is_clear(I_BUTTONPIN,I_BUTTON)){
step_i(j);
set_i();
updatedisplay();
updaters232();
ignorebutton=15000;
}
if (bit_is_clear(U_BUTTONPIN,U_BUTTON)){
if (j==0){
uval+=1;
}else{
uval-=1;
}
set_u();
updatedisplay();
updaters232();
ignorebutton=15000;
}
if (bit_is_clear(UUBUTTONPIN,UUBUTTON)){
if (j==0){
uval+=10;
}else{
uval-=10;
}
set_u();
updatedisplay();
updaters232();
ignorebutton=15000;
}
if (bit_is_clear(SBBUTTONPIN,SBBUTTON)){
if (mode != 2){
mode=2;
}else{
mode=0; /* if i-limit is active then this will change below */
}
toggle_standby();
ignorebutton=20000;
}
/* now check down (--) buttons */
cbi(BUTTONDOWNPORT,BUTTONDOWN);
sbi(BUTTONUPPORT,BUTTONUP);
j++;
}
if (ignorebutton) ignorebutton--;
if (ilimitdebounce) ilimitdebounce--;
if (mode!=2){ /* not standby */
if (bit_is_clear(ILIMITPIN,ILIMIT)){
mode=1;
ilimitdebounce=200;
/* red led on */
cbi(ILEDPORT,ILED);
}else{
if(mode==1 && ilimitdebounce==0){
mode=0;
/* red led off */
sbi(ILEDPORT,ILED);
}
}
}
if (mode!=oldmode){
updatedisplay();
updaters232();
}
oldmode=mode;
/* flash red led in standby */
if (mode==2){
if (blink > 15000){
blink=0;
}
if (blink <2000){
cbi(ILEDPORT,ILED); /* red led on */
}else{
sbi(ILEDPORT,ILED); /* red led off */
}
blink++;
}
rxresult=uart_getrxbufnl(uart_outbuf);
if (rxresult==0){
continue;
}
/* valid command are:
* i=0-3000 u=0-300 s=0-1 s=? u=? i=? */
/* now parse the comands and act on them */
if (strlen(uart_outbuf) < 3) {
reply_err();
continue;
}
if (uart_outbuf[1] != '='){
reply_err();
continue;
}
val=&(uart_outbuf[2]);
cmd=uart_outbuf[0];
status=0;
/* act on the commands */
if (cmd=='i'){
if (*val== '?'){
updaters232();
continue;
}
ival=(int)atoi(val);
set_i();
updatedisplay();
status=1;
}
/* act on the commands */
if (cmd=='u'){
if (*val== '?'){
updaters232();
continue;
}
uval=(int)atoi(val);
set_u();
updatedisplay();
status=1;
}
if (cmd=='s'){
i=*val;
if (i == '?'){
updaters232();
continue;
}
if (i == '1'){
mode=2;
status=1;
}
if (i == '0'){
mode=0; /* if i-limit is active then this will change in the next loop */
status=1;
}
toggle_standby();
}
/* command handling done. now return status */
if (status==1){
reply_ok();
}else{
reply_err();
}
}
}
das ganze steuert ein labornetzteil....laut der datenblätter ist die pinbelegung gleich.... (es sind nur einige zusatzfunktionen dazugekommen..vgl:
Lesezeichen