/*
  SpStuuur

  De software voor SpStuur

  Pros 2003 ... 2008
*/


#define F_CPU 14745600UL

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <compat/deprecated.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>


typedef unsigned char u08;
typedef unsigned short u16;

/*
  Weerstanden 47k 15k 8k2 4k7 1k5 100R
  
  Spanningen
  950, 854, 714, 580, 379, 134
*/


#define V1 1000
#define V5 (V1 * 10)
#define Uplus 854
#define UV1plus 714
#define UV5plus 580
#define Umin 379
#define UV1min 134
#define UV5min 134

/*
  De uitgangen
*/


#define BIT0                    PC3
#define BIT1                    PC2
#define BIT2                    PC1
#define BIT3                    PD4
#define BIT4                    PC0
#define BIT5                    PD5
#define BIT6                    PD6
#define BIT7                    PD7

#define BIT0_PORT               PORTC
#define BIT1_PORT               PORTC
#define BIT2_PORT               PORTC
#define BIT3_PORT               PORTD
#define BIT4_PORT               PORTC
#define BIT5_PORT               PORTD
#define BIT6_PORT               PORTD
#define BIT7_PORT               PORTD

#define BIT0_DDR                DDRC
#define BIT1_DDR                DDRC
#define BIT2_DDR                DDRC
#define BIT3_DDR                DDRD
#define BIT4_DDR                DDRC
#define BIT5_DDR                DDRD
#define BIT6_DDR                DDRD
#define BIT7_DDR                DDRD

#define BEEP                    PD3
#define BUFCLK                  PB2
#define BUFCLK_L                PC4
#define BUFCLK_H                PB1
#define BEEP_PORT               PORTD
#define BUFCLK_PORT             PORTB
#define BUFCLK_L_PORT           PORTC
#define BUFCLK_H_PORT           PORTB
#define BEEP_DDR                DDRD
#define BUFCLK_DDR              DDRB
#define BUFCLK_L_DDR            DDRC
#define BUFCLK_H_DDR            DDRB

// De pinnen die het LCD-display bedienen

#define LCD_PORT_D7             PORTD
#define LCD_PORT_D6             PORTD
#define LCD_PORT_D5             PORTD
#define LCD_PORT_D4             PORTC

#define LCD_PIN_D7              PIND
#define LCD_PIN_D6              PIND
#define LCD_PIN_D5              PIND
#define LCD_PIN_D4              PINC

#define LCD_DDR_D7              DDRD
#define LCD_DDR_D6              DDRD
#define LCD_DDR_D5              DDRD
#define LCD_DDR_D4              DDRC

#define LCD_D7                  PD7
#define LCD_D6                  PD6
#define LCD_D5                  PD5
#define LCD_D4                  PC0

#define LCD_RS_PORT             PORTC                           /* port for RS line */
#define LCD_RS_DDR              DDRC                            /* ddr for RS line */
#define LCD_RS                  PC3
#define LCD_RW_PORT             PORTC                           /* port for RW line */
#define LCD_RW_DDR              DDRC                            /* ddr for RW line */
#define LCD_RW                  PC2
#define LCD_E_PORT              PORTB                           /* port for Enable line */
#define LCD_E_DDR               DDRB                            /* ddr for Enable line */
#define LCD_E                   PB0

/* instruction register bit positions */
#define LCD_CLR             0                                   /* DB0: clear display */
#define LCD_HOME            1                                   /* DB1: return to home position */
#define LCD_ENTRY_MODE      2                                   /* DB2: set entry mode */
#define LCD_ENTRY_INC       1                                   /* DB1: 1=increment, 0=decrement  */
#define LCD_ENTRY_SHIFT     0                                   /* DB2: 1=display shift on        */
#define LCD_ON              3                                   /* DB3: turn lcd/cursor on */
#define LCD_ON_DISPLAY      2                                   /* DB2: turn display on */
#define LCD_ON_CURSOR       1                                   /* DB1: turn cursor on */
#define LCD_ON_BLINK        0                                   /* DB0: blinking cursor ? */
#define LCD_MOVE            4                                   /* DB4: move cursor/display */
#define LCD_MOVE_DISP       3                                   /* DB3: move display (0-> cursor) ? */
#define LCD_MOVE_RIGHT      2                                   /* DB2: move right (0-> left) ? */
#define LCD_FUNCTION        5                                   /* DB5: function set */
#define LCD_FUNCTION_8BIT   4                                   /* DB4: set 8BIT mode (0->4BIT mode) */
#define LCD_FUNCTION_2LINES 3                                   /* DB3: two lines (0->one line) */
#define LCD_FUNCTION_10DOTS 2                                   /* DB2: 5x10 font (0->5x7 font) */
#define LCD_CGRAM           6                                   /* DB6: set CG RAM address */
#define LCD_DDRAM           7                                   /* DB7: set DD RAM address */
#define LCD_BUSY            7                                   /* DB7: LCD is busy */

/* set entry mode: display shift on/off, dec/inc cursor move direction */
#define LCD_ENTRY_DEC            0x04                           /* display shift off, dec cursor move dir */
#define LCD_ENTRY_DEC_SHIFT      0x05                           /* display shift on,  dec cursor move dir */
#define LCD_ENTRY_INC_           0x06                           /* display shift off, inc cursor move dir */
#define LCD_ENTRY_INC_SHIFT      0x07                           /* display shift on,  inc cursor move dir */

/* display on/off, cursor on/off, blinking char at cursor position */
#define LCD_DISP_OFF             0x08                           /* display off                            */
#define LCD_DISP_ON              0x0C                           /* display on, cursor off                 */
#define LCD_DISP_ON_BLINK        0x0D                           /* display on, cursor off, blink char     */
#define LCD_DISP_ON_CURSOR       0x0E                           /* display on, cursor on                  */
#define LCD_DISP_ON_CURSOR_BLINK 0x0F                           /* display on, cursor on, blink char      */

/* move cursor/shift display */
#define LCD_MOVE_CURSOR_LEFT     0x10                           /* move cursor left  (decrement)          */
#define LCD_MOVE_CURSOR_RIGHT    0x14                           /* move cursor right (increment)          */
#define LCD_MOVE_DISP_LEFT       0x18                           /* shift display left                     */
#define LCD_MOVE_DISP_RIGHT      0x1C                           /* shift display right                    */

/* function set: set interface data length and number of display lines */
#define LCD_FUNCTION_4BIT_1LINE  0x20                           /* 4-bit interface, single line, 5x7 dots */
#define LCD_FUNCTION_4BIT_2LINES 0x28                           /* 4-bit interface, dual line,   5x7 dots */
#define LCD_FUNCTION_8BIT_1LINE  0x30                           /* 8-bit interface, single line, 5x7 dots */
#define LCD_FUNCTION_8BIT_2LINES 0x38                           /* 8-bit interface, dual line,   5x7 dots */

#define LCD_MODE_DEFAULT     ((1<<LCD_ENTRY_MODE) | (1<<LCD_ENTRY_INC))

/* normally you do not change the following */
#define LCD_LINES           2                                   /* visible lines */
#define LCD_LINE_LENGTH  0x28                                   /* internal line length */
#define LCD_START_LINE1  0x00                                   /* DDRAM address of first char of line 1 */
#define LCD_START_LINE2  0x28                                   /* DDRAM address of first char of line 2 */

#define UART_RX_BUFFER_SIZE 23


#define P(s) ({static const char c[] __attribute__ ((progmem)) = s;c;})
#define lcd_puts_P(__s)         lcd_puts_p(P(__s))


#define lcd_e_delay()   _delay_us(1);
#define lcd_e_high()    sbi(LCD_E_PORT, LCD_E)
#define lcd_e_low()     cbi(LCD_E_PORT, LCD_E)
#define lcd_e_toggle()  toggle_e()

#define lcd_cmd_mode()  cbi(LCD_RS_PORT, LCD_RS)                /* RS=0  command mode   */
#define lcd_data_mode() sbi(LCD_RS_PORT, LCD_RS)                /* RS=1  data mode      */
#define lcd_wr_mode()   cbi(LCD_RW_PORT, LCD_RW)                /* RW=0  write mode     */
#define lcd_rd_mode()   sbi(LCD_RW_PORT, LCD_RW)                /* RW=1  read mode      */


#define LCD_FUNCTION_DEFAULT    LCD_FUNCTION_4BIT_2LINES


volatile static int ADC_in;
static char uart_outbuf[UART_RX_BUFFER_SIZE + 1];
static char uart_rxbuf[UART_RX_BUFFER_SIZE + 1];
volatile static unsigned char uart_rx_pos;      /* end of string in rx */
volatile static unsigned char uart_rx_linecomplete;     /* one complete line in buffer */
unsigned int Spanning;
unsigned char ijrxresultstatusInitDisplay;
char cmd;
char *val;
char asciistring[10];


#define delay(x) _delay_loop_1(x)
#define disable_INT0() cbi(GICR, INT0)


void delay_ms(unsigned int ms)
{
    for (; ms > 0ms--) {
        _delay_ms(0.98);
    }
}


void delay_us(unsigned int us)
{
    for (; us > 0us--) {
        _delay_us(0.9);
    }
}


// Init PD2 als INT0
void enable_INT0(void)
{
    cbi(DDRDPD2);                                             // PD2 = ingang
    sbi(PORTDPD2);                                            // Pull-up weerstand activeren
    sbi(MCUCRISC01);                                          // The falling edge of INT0 generates an interrupt request.
    sbi(GICRINT0);
}


void u2ascii(u16 d)
{
    unsigned char tmp;

    /*
       transformeer een unsigned short naar ascii,
       uitgedrukt in mVolt.

       123  wordt 00.123mV
       4567 wordt 04.567mV
     */


    /* d omzetten naar mVolt */
    d /= 2;
    tmp = d / 10000;
    asciistring[0] = tmp + '0';
    d -= (tmp * 10000);
    tmp = d / 1000;                                             /* tmp = 1 */
    asciistring[1] = tmp + '0';
    asciistring[2] = '.';
    d -= (tmp * 1000);                                          /* d = 234 */
    tmp = d / 100;                                              /* tmp = 2 */
    asciistring[3] = tmp + '0';
    d -= (tmp * 100);                                           /* d = 34 */
    tmp = d / 10;                                               /* tmp = 3 */
    asciistring[4] = tmp + '0';
    d -= (tmp * 10);                                            /* d = 4 */
    asciistring[5] = d + '0';
    asciistring[6] = 'm';
    asciistring[7] = 'V';
    asciistring[8] = 0;
}


/* return analog value of a given channel. You must enable interrupt with
* sei() in the main program */

int convertanalog(void)
{
    sbi(ADCSRAADSC);                                          // start conversion
    while ((ADCSRA & _BV(ADSC)) != 0) {                         // Effe wachten tot ADSC laag is
    }
    ADC_in = ADC;
    return (ADC_in);
}


/* defines all data pins as input */
void lcd_data_port_in(void)
{
    cbi(LCD_DDR_D7LCD_D7);
    cbi(LCD_DDR_D6LCD_D6);
    cbi(LCD_DDR_D5LCD_D5);
    cbi(LCD_DDR_D4LCD_D4);
    sbi(LCD_RS_DDRLCD_RS);
    sbi(LCD_RW_DDRLCD_RW);
    sbi(LCD_E_DDRLCD_E);
}

/* defines all data pins as output */
void lcd_data_port_out(void)
{
    sbi(LCD_DDR_D7LCD_D7);
    sbi(LCD_DDR_D6LCD_D6);
    sbi(LCD_DDR_D5LCD_D5);
    sbi(LCD_DDR_D4LCD_D4);
    sbi(LCD_RS_DDRLCD_RS);
    sbi(LCD_RW_DDRLCD_RW);
    sbi(LCD_E_DDRLCD_E);
}

static void lcd_out_low(u08 d)
{                                                               /* output low nibble */
    if (d & 0x08)
        sbi(LCD_PORT_D7LCD_D7);
    else
        cbi(LCD_PORT_D7LCD_D7);
    if (d & 0x04)
        sbi(LCD_PORT_D6LCD_D6);
    else
        cbi(LCD_PORT_D6LCD_D6);
    if (d & 0x02)
        sbi(LCD_PORT_D5LCD_D5);
    else
        cbi(LCD_PORT_D5LCD_D5);
    if (d & 0x01)
        sbi(LCD_PORT_D4LCD_D4);
    else
        cbi(LCD_PORT_D4LCD_D4);
}


static void lcd_out_high(u08 d)
{                                                               /* output high nibble */
    if (d & 0x80)
        sbi(LCD_PORT_D7LCD_D7);
    else
        cbi(LCD_PORT_D7LCD_D7);
    if (d & 0x40)
        sbi(LCD_PORT_D6LCD_D6);
    else
        cbi(LCD_PORT_D6LCD_D6);
    if (d & 0x20)
        sbi(LCD_PORT_D5LCD_D5);
    else
        cbi(LCD_PORT_D5LCD_D5);
    if (d & 0x10)
        sbi(LCD_PORT_D4LCD_D4);
    else
        cbi(LCD_PORT_D4LCD_D4);
}

static void toggle_e(void)
/* toggle Enable Pin */
{
    lcd_e_high();
    lcd_e_delay();
    lcd_e_low();
}


static void lcd_write(u08 datau08 rs)
{
    /* configure data pins as output */
    lcd_data_port_out();

    /* output high nibble first */

    lcd_out_high(data);

    if (rs)
        lcd_data_mode();                                        /* RS=1: write data            */
    else
        lcd_cmd_mode();                                         /* RS=0: write instruction     */
    lcd_wr_mode();                                              /* RW=0  write mode         */
    lcd_e_toggle();

    /* output low nibble */
    lcd_out_low(data);

    if (rs)
        lcd_data_mode();                                        /* RS=1: write data            */
    else
        lcd_cmd_mode();                                         /* RS=0: write instruction     */
    lcd_wr_mode();                                              /* RW=0  write mode         */

    lcd_e_toggle();

    /* all data pins high (inactive) */
    lcd_data_port_in();
}



static u08 lcd_read(u08 rs)
{
    register u08 data;

    /* configure data pins as input */
    lcd_data_port_in();

    if (rs)
        lcd_data_mode();                                        /* RS=1: read data      */
    else
        lcd_cmd_mode();                                         /* RS=0: read busy flag */
    lcd_rd_mode();                                              /* RW=1  read mode      */

    lcd_e_high();
    lcd_e_delay();

    /* read high nibble first */
    data = bit_is_set(LCD_PIN_D7LCD_D7) ? 0x80 : 0;
    data |= bit_is_set(LCD_PIN_D6LCD_D6) ? 0x40 : 0;
    data |= bit_is_set(LCD_PIN_D5LCD_D5) ? 0x20 : 0;
    data |= bit_is_set(LCD_PIN_D4LCD_D4) ? 0x10 : 0;

    lcd_e_low();
    lcd_e_delay();                                              /* Enable low       */
    lcd_e_high();
    lcd_e_delay();

    /* read low nibble        */
    data |= bit_is_set(LCD_PIN_D7LCD_D7) ? 0x08 : 0;
    data |= bit_is_set(LCD_PIN_D6LCD_D6) ? 0x04 : 0;
    data |= bit_is_set(LCD_PIN_D5LCD_D5) ? 0x02 : 0;
    data |= bit_is_set(LCD_PIN_D4LCD_D4) ? 0x01 : 0;

    lcd_e_low();

    return (data);
}


static unsigned char lcd_waitbusy(void)
/* loops while lcd is busy, reads address counter */
{
    register unsigned char c;

    while ((c = lcd_read(0)) & (1 << LCD_BUSY)) {
    }

    return (c);                                                 // return address counter=position
}


void lcd_command(u08 cmd)
/* send commando <cmd> to LCD */
{
    lcd_data_port_out();                                        /* all data port bits as output */
    lcd_waitbusy();
    lcd_write(cmd0);
}


void lcd_gotoxy(u08 xu08 y)
/* goto position (x,y) */
{
    if (y == 0)
        lcd_command((1 << LCD_DDRAM) + LCD_START_LINE1 + x);
    else
        lcd_command((1 << LCD_DDRAM) + LCD_START_LINE2 + x);
}


void lcd_clrscr(void)
/* clear lcd and set cursor to home position */
{
    lcd_command(1 << LCD_CLR);
}


void lcd_home(void)
/* set cursor to home position */
{
    lcd_command(1 << LCD_HOME);
}


void lcd_putc(char c)
/* print character at current cursor position */
{
    lcd_waitbusy();                                             // read busy-flag and address counter
    lcd_write((unsigned charc1);
}


void lcd_puts(const char *s)
/* print string on lcd  */
{
    while (*s) {
        lcd_putc(*s);
        s++;
    }
}


void lcd_puts_p(const prog_char * progmem_s)
/* print string from program memory on lcd  */
{
    register char c;

    while ((c = pgm_read_byte(progmem_s++))) {
        lcd_putc(c);
    }
}

/*
  Een soort atof(), waarbij het resultaat een int is.
  We verwachten 0 tot 3 cijfers na de komma; als er meer
  zijn, worden die genegeerd.
*/

unsigned int my_atoi(const char *s)
{
    unsigned int tmpx;
    unsigned char komma = 'n';

    x = 0;

    while (*s) {
        if ((*s >= '0') && (*s <= '9')) {
            tmp = *s - '0';
            x = (x * 10) + tmp;
            s++;
            if (komma < 4) {
                komma++;
            } else if (komma == 4) {
                break;
            }
        } else if ((*s == ',') || (*s == '.')) {
            komma = 0;
            s++;
        } else {
            break;
        }
    }

    switch (komma) {
    case 0:
    case 'n':
        x *= 1000;
        break;
    case 1:
        x *= 100;
        break;
    case 2:
        x *= 10;
        break;
    }

    if (x > 0x7FFe) {
        x = 0x7FFE;
    }
    return (x * 2);
}

void io_out(void)
{
    sbi(BIT7_DDRBIT7);
    sbi(BIT6_DDRBIT6);
    sbi(BIT5_DDRBIT5);
    sbi(BIT4_DDRBIT4);
    sbi(BIT3_DDRBIT3);
    sbi(BIT2_DDRBIT2);
    sbi(BIT1_DDRBIT1);
    sbi(BIT0_DDRBIT0);
}



void lcd_init(u08 dispAttr)
/* initialize display and select type of cursor
 dispAttr: LCD_DISP_OFF, LCD_DISP_ON, LCD_DISP_ON_CURSOR, LCD_DISP_CURSOR_BLINK
*/

{
    /*------ Initialize lcd to 4 bit i/o mode -------*/

    lcd_data_port_out();                                        /* all data port bits as output */
    sbi(LCD_RS_DDRLCD_RS);                                    /* RS pin as output */
    sbi(LCD_RW_DDRLCD_RW);                                    /* RW pin as output */
    sbi(LCD_E_DDRLCD_E);                                      /* E  pin as output */
    delay_us(16000);                                            /* wait 16ms or more after power-on       */

    /* initial write to lcd is 8bit */
    lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
    lcd_e_toggle();
    delay_us(4992);                                             /* delay, busy flag can't be checked here */

    lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
    lcd_e_toggle();
    delay_us(64);                                               /* delay, busy flag can't be checked here */

    lcd_out_high(LCD_FUNCTION_8BIT_1LINE);
    lcd_e_toggle();
    delay_us(64);                                               /* delay, busy flag can't be checked here */

    lcd_out_high(LCD_FUNCTION_4BIT_1LINE);                      /* set IO mode to 4bit */
    lcd_e_toggle();

    /* from now the lcd only accepts 4 bit I/O, we can use lcd_command() */

    lcd_command(LCD_FUNCTION_DEFAULT);                          /* function set: display lines  */
    lcd_command(LCD_DISP_OFF);                                  /* display off                  */
    lcd_clrscr();                                               /* display clear                */
    lcd_command(LCD_MODE_DEFAULT);                              /* set entry mode               */
    lcd_command(dispAttr);                                      /* display/cursor control       */
}



// Deze functie wordt aangeroepen, telkens er een karakter ontvangen werd
SIGNAL(SIG_UART_RECV)
{
    uart_rxbuf[uart_rx_pos] = UDR;
    if ((uart_rxbuf[uart_rx_pos] == '\n') || (uart_rx_pos > UART_RX_BUFFER_SIZE - 1)) {
        uart_rxbuf[uart_rx_pos] = 0;
        strcpy(uart_outbufuart_rxbuf);
        uart_rx_linecomplete = 1;
        uart_rx_pos = 0;
    } else {
        uart_rx_pos++;
    }
}



void uart_init(void)
{
    uart_rx_pos = 0;
    uart_rx_linecomplete = 0;
    UCSRB = _BV(TXEN) | _BV(RXEN) | _BV(RXCIE);                 // enable tx/rx and interrupt on rx
    UCSRC = _BV(URSEL) | _BV(UCSZ1) | _BV(UCSZ0);               // Set frame format: 8N1
    UBRRH = 0;                                                  // set baudrate (9600, 19200, 38400, 57600, 76800, 115200, 230400)
    UBRRL = (F_CPU / (16 * 38400UL)) - 1;
}


//  Wait until tx holding register is empty, then send character c down the UART Tx.
void uart_sendchar(unsigned char c)
{
    loop_until_bit_is_set(UCSRAUDRE);
    UDR = c;
}


/* send string to the rs232 */
void uart_sendstr(char *s)
{
    while (*s) {
        uart_sendchar(*s);
        s++;
    }
}


void uart_sendstr_p(const prog_char * progmem_s)
/* print string from program memory on rs232 */
{
    char c;

    while ((c = pgm_read_byte(progmem_s++))) {
        uart_sendchar(c);
    }
}

#define uart_sendstr_P(__s)         uart_sendstr_p(P(__s))

void reply(int r)
{
    if (r == 1) {
        uart_sendstr_P("ok\n");
    } else if (r == 0) {
        uart_sendstr_P("\n\nGeldige opdrachten:\n\n");
        uart_sendstr_P("u=x of s=x zet de spanning op x\n");
        uart_sendstr_P("u=+ u=- vermeerder of verminder de spanning met 1mV\n");
        uart_sendstr_P("u=? toon de ingestelde spanning\n");
    }
}


void beep(unsigned int frequnsigned int dur)
{
    unsigned int cntdeltimes;

    /*
       We gebruiken bit 7 om de beeper te sturen.

       freq wordt uitgedrukt in Hz
       dur wordt uitgedrukt in mSec
       del is de delay tussen 2 flanken
       times is het aantal cycli
       resolutie = 1µSec


       Voorbeeld: freq = 2000
       dur = 5000
       del = 500000 / 2000 = 250
       dur = dur * 1000 = 5000000
       times = 5000000 / 500 = 10000
     */


    del = 500000 / freq;
    dur *= 10;
    times = dur / (del / 50);

    sbi(BEEP_DDRBEEP);

    for (cnt = 0cnt < timescnt++) {
        sbi(BEEP_PORTBEEP);
        delay_us(del);
        cbi(BEEP_PORTBEEP);
        delay_us(del);
    }
}

void bip(void)
{
    unsigned int cnt;

    sbi(BEEP_DDRBEEP);

    for (cnt = 0cnt < 6cnt++) {
        sbi(BEEP_PORTBEEP);
        delay_us(1000);
        cbi(BEEP_PORTBEEP);
        delay_us(1000);
    }
}


void Sp2Displ(u16 d)
{
    if (InitDisplay == 'y') {
        InitDisplay = 'n';
        lcd_clrscr();
        lcd_puts_P("Ingestelde spanning:");
    }
    lcd_gotoxy(331);
    u2ascii(d);
    lcd_puts(asciistring);
}


/* Zet de spanning op 0 */
void out0(void)
{
    io_out();
    cbi(BUFCLK_H_PORTBUFCLK_H);
    cbi(BUFCLK_PORTBUFCLK);
    cbi(BUFCLK_L_PORTBUFCLK_L);
    cbi(BIT7_PORTBIT7);
    cbi(BIT6_PORTBIT6);
    cbi(BIT5_PORTBIT5);
    cbi(BIT4_PORTBIT4);
    cbi(BIT3_PORTBIT3);
    cbi(BIT2_PORTBIT2);
    cbi(BIT1_PORTBIT1);
    cbi(BIT0_PORTBIT0);
    sbi(BUFCLK_H_PORTBUFCLK_H);
    sbi(BUFCLK_L_PORTBUFCLK_L);
    sbi(BUFCLK_PORTBUFCLK);
}


void wr_spanning(u16 d)
{
    io_out();

    /* Eerst D15 ... D08 */

    cbi(BUFCLK_H_PORTBUFCLK_H);
    if (d & 0x8000)
        sbi(BIT7_PORTBIT7);
    else
        cbi(BIT7_PORTBIT7);
    if (d & 0x4000)
        sbi(BIT6_PORTBIT6);
    else
        cbi(BIT6_PORTBIT6);
    if (d & 0x2000)
        sbi(BIT5_PORTBIT5);
    else
        cbi(BIT5_PORTBIT5);
    if (d & 0x1000)
        sbi(BIT4_PORTBIT4);
    else
        cbi(BIT4_PORTBIT4);
    if (d & 0x0800)
        sbi(BIT3_PORTBIT3);
    else
        cbi(BIT3_PORTBIT3);
    if (d & 0x0400)
        sbi(BIT2_PORTBIT2);
    else
        cbi(BIT2_PORTBIT2);
    if (d & 0x0200)
        sbi(BIT1_PORTBIT1);
    else
        cbi(BIT1_PORTBIT1);
    if (d & 0x0100)
        sbi(BIT0_PORTBIT0);
    else
        cbi(BIT0_PORTBIT0);
    sbi(BUFCLK_H_PORTBUFCLK_H);

    /* Vervolgens D07 ... D00 */

    cbi(BUFCLK_PORTBUFCLK);
    cbi(BUFCLK_L_PORTBUFCLK_L);
    if (d & 0x0080)
        sbi(BIT7_PORTBIT7);
    else
        cbi(BIT7_PORTBIT7);
    if (d & 0x0040)
        sbi(BIT6_PORTBIT6);
    else
        cbi(BIT6_PORTBIT6);
    if (d & 0x0020)
        sbi(BIT5_PORTBIT5);
    else
        cbi(BIT5_PORTBIT5);
    if (d & 0x0010)
        sbi(BIT4_PORTBIT4);
    else
        cbi(BIT4_PORTBIT4);
    if (d & 0x0008)
        sbi(BIT3_PORTBIT3);
    else
        cbi(BIT3_PORTBIT3);
    if (d & 0x0004)
        sbi(BIT2_PORTBIT2);
    else
        cbi(BIT2_PORTBIT2);
    if (d & 0x0002)
        sbi(BIT1_PORTBIT1);
    else
        cbi(BIT1_PORTBIT1);
    if (d & 0x0001)
        sbi(BIT0_PORTBIT0);
    else
        cbi(BIT0_PORTBIT0);
    sbi(BUFCLK_L_PORTBUFCLK_L);
    lcd_e_delay();
    sbi(BUFCLK_PORTBUFCLK);

    Sp2Displ(d);
    bip();
}


/*
  Initialiseer input en output.
*/

void io_init(void)
{
    // We moeten zo snel mogelijk 0x0000 op de uitgangsbuffer plaatsen.
    io_out();
    sbi(BUFCLK_DDRBUFCLK);
    sbi(BUFCLK_L_DDRBUFCLK_L);
    sbi(BUFCLK_H_DDRBUFCLK_H);
    sbi(BUFCLK_H_PORTBUFCLK_H);
    sbi(BUFCLK_L_PORTBUFCLK_L);
    sbi(BUFCLK_PORTBUFCLK);
    Spanning = 0;
    //    out0();
}


void init(void)
{
    io_init();                                                  // io-poorten initialiseren, spanning op 0 zetten
    lcd_init(LCD_DISP_ON);                                      // initialize display, cursor off
    uart_init();                                                // initialize rs232
    enable_INT0();                                              // De noodstop-knop komt op INT0
    sei();                                                      /* enable global interrupt */
    lcd_clrscr();
    ADCSRA = _BV(ADEN) | _BV(ADIF) | _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0);
    ADMUX = 5 | _BV(REFS0);                                     // Init ADC
    sbi(ADCSRAADSC);                                          // start dummy conversion
    beep(1000200);
    lcd_puts_P("SpStuur init done.");
    lcd_gotoxy(351);
    lcd_puts_P("Pros 2004");
    InitDisplay = 'y';
    beep(800200);
    delay_ms(5000);
}


SIGNAL(SIG_INTERRUPT0)
{
    out0();                                                     // Uitgangsspanning op 0 zetten
    Spanning = 0;
    Sp2Displ(0);
    beep(1200220);
    delay_ms(1000);
    while (bit_is_clear(PINDPIND2)) {
        delay_ms(100);
    }
    delay_ms(1000);
    beep(1200220);
}


void check_ADC(void)
{
    unsigned int wt;

    /*
       We gebruiken PC5 als analoge ingang.
       Deze is via een pull-up weerstand verbonden met Vcc.
       6 toetsen trekken - ieder via hun eigen specifieke weerstand -
       de spanning op PC5 naar beneden.
       Dus, als het resultaat van convertanalog() kleiner is dan
       pakweg 950 (1023 is de maximum), kunnen we er van uitgaan
       dat iemand een toets indrukte.
       Voor alle zekerheid wachten we even, en testen nogmaals.
     */


    /* Wacht enkele mSec */
    delay_ms(100);
    wt = 500;

    while (convertanalog() < 950) {
        /* Nu zijn we zeker: er is een knop ingedrukt.

           De pull-up weerstand bedraagt 4k7.
           De toets-weerstanden zijn: 47k, 15k, 8k2, 4k7, 1k5, 100R en 27R voor de noodstop
           Dat levert de volgende metingen op:
           930, 779, 650, 511, 247, 21
           De grensovergangen hiertussen zijn:
           854, 714, 580, 379, 134
         */

        if (ADC_in > Uplus) {
            if (Spanning < 0xFFFE) {
                Spanning += 2;
                wr_spanning(Spanning);
            } else {
                return;
            }
        } else if (ADC_in > UV1plus) {
            if (Spanning < (0xFFFE - V1)) {
                Spanning += V1;
                wr_spanning(Spanning);
            } else {
                Spanning = 0xFFFE;
                wr_spanning(Spanning);
                return;
            }
        } else if (ADC_in > UV5plus) {
            if (Spanning < (0xFFFE - V5)) {
                Spanning += V5;
                wr_spanning(Spanning);
                // Als we de spanning met 5Volt verhogen, gaan we wat omzichtiger te werk. Even te lang
                // de knop indrukken als we een TTL-circuit met 5V willen voeden, kan nare gevolgen hebben!
                delay_ms(800);
            } else {
                Spanning = 0xFFFE;
                wr_spanning(Spanning);
                return;
            }
        } else if (ADC_in > Umin) {
            if (Spanning > 1) {
                Spanning -= 2;
                wr_spanning(Spanning);
            } else {
                return;
            }
        } else if (ADC_in > UV1min) {
            if (Spanning > V1) {
                Spanning -= V1;
                wr_spanning(Spanning);
            } else {
                Spanning = 0;
                wr_spanning(Spanning);
                return;
            }
        } else {
            if (Spanning > V5) {
                Spanning -= V5;
                wr_spanning(Spanning);
            } else {
                Spanning = 0;
                wr_spanning(Spanning);
                return;
            }
        }
        if (wt > 1) {
            delay_ms(wt);
            wt *= 9;
            wt /= 10;
        } else {
            delay_ms(1);
        }
    }
}


void check_RS232(void)
{
    /*
       Geldige opdrachten:

       u=x of s=x zet de spanning op x
       u=+ u=- vermeerder of verminder de spanning met 1mV
       u=? toon de ingestelde spanning
     */

    if (strncmp(uart_outbuf"Who"3) == 0) {
        uart_sendstr_P("SpStuur\n");
    } else if (uart_outbuf[1] != '=') {
        reply(0);
    } else {
        val = &(uart_outbuf[2]);
        status = 0;
        switch (uart_outbuf[0]) {
        case 'u':
        case 'U':
        case 's':
        case 'S':
            if ((Spanning < 0xFFFD) && (*val == '+')) {         // Spanning verhogen met 1 mV
                Spanning += 2;
                wr_spanning(Spanning);
                status = 3;
            } else if ((Spanning > 1) && (*val == '-')) {       // Spanning verlagen met 1 mV
                Spanning--;
                wr_spanning(Spanning);
                status = 3;
            } else if ((*val >= '0') && (*val <= '9')) {        // De opgegeven spanning instellen
                Spanning = my_atoi(val);
                if (Spanning > 0xFFFE) {
                    Spanning = 0xFFFE;
                }
                wr_spanning(Spanning);
                status = 2;
            }
            u2ascii(Spanning);
            uart_sendstr(asciistring);                          //  De ingestelde spanning over de seriele lijn versturen
            uart_sendchar('\n');
            status = 2;
        }
        reply(status);
    }
}


int main(void)
{

    init();

    while (1) {
        if (convertanalog() < 950) {                            // Toetsen testen
            check_ADC();
        }
        if (uart_rx_linecomplete == 1) {                        // RS232 testen
            check_RS232();
            uart_rx_linecomplete = 0;
        }
    }
}