LNLS Driver Heaters DCM Firmware with two sockets: - HTTP server at port 80 - TCP socket at port 5757 for EPICS

Dependencies:   mbed mbed-rtos AMC7812B EthernetInterface TextLCD

Firmware for DCM heaters driver - 8 channel, 12V/1.5A, PWM controlled with interlock and failure diagnostics

Mauricio Martins Donatti

Brazilian Synchrotron Light Laboratory

Electronics Support Group - GAE

mauricio.donatti@lnls.br

Extra scripts on Github page:

1. Driver_DCM_Socket.py: Open a socket and comunicate with the Driver.

2. Find_IP.py: Search a subdomain for devices (IPs) with a TCP Server Socket on the specified port.

3. firmware.txt: device firmware link (MBED).

4. index.html: HTTP server GET answer - html + JS.

main.cpp

Committer:
mmdonatti
Date:
2021-08-06
Revision:
3:820e3a42c06b
Parent:
2:7a49ed074968

File content as of revision 3:820e3a42c06b:

/*********************************************************************
*
* Driver Heaters DCM 8 channel - v4 (improvements)
* 
* Mauricio Martins Donatti
* mauricio.donatti@lnls.br
*
* Electronic Support Group - GAE
* Brazilian Synchrotron Light Laboratory (LNLS)
* Brazilian Center for Research in Energy and Materials (CNPEM)
*
* Jan 2021
*
* References:
*   - AMC7812B datasheet: http://www.ti.com/lit/ds/symlink/amc7812b.pdf
*   - Modification inside ethernet library: https://os.mbed.com/questions/1602/How-to-set-the-TCPIP-stack-s-hostname-pr/
*   - VERY IMPORTANT: https://os.mbed.com/questions/61544/threaded-TCP-server-with-multi-port-it-r/
*
*******************************************************************/

#define SUPPLY_24V
//#define CAL

#include "mbed.h"
#include "TextLCD.h"
#include "EthernetInterface.h"
#include "amc7812b.h"

#define ERROR_INTERFACE
//#define DEBUG_INTERFACE

#include "definitions.h"
#include "HTTP_SERVER.h"

#define VERSION             "v4.1-2021-07\0"        //Firmware Version
#define DEVICE_NAME         "DH-8CH-001\0"          //Device Name - Ethernet default hostname
 
#define PORT    6767                                //Socket Port (EPICS data)

//SPI constructor
SPI spi(MOSI,MISO,SCLK);                            //mosi, miso, sclk

Serial pc(USBTX, USBRX, 115200);

//Digital Output constructors
DigitalOut cs_u4(CS_U4);
DigitalOut cs_u5(CS_U5);
DigitalOut amc_reset(RST);
DigitalOut dac_clr(DAC_CLR);
DigitalOut cnvt_u5(CNVT_U5);
DigitalOut cnvt_u4(CNVT_U4);

//Digital Input constructors
DigitalIn al_u4(AL_U4);
DigitalIn al_u5(AL_U5);

//Interrupt Input constructors
InterruptIn dav_u4(DAV_U4);
InterruptIn dav_u5(DAV_U5);
 
//Digital output indicators
DigitalOut blink(LED4);                 //Heartbeat at LED4
DigitalOut socket_led(LED2);            //Socket Connection LED
DigitalOut led_cfg(LED3);               //Configuration LED - ON only in configuration mode (adjusting current limit)
DigitalOut server_led(LED1);            //HTTP Server LED
DigitalOut phy_rst(P1_28);              //Ethernet PHY reset
DigitalOut eth_led1(p29);                //Ethernet LED1
DigitalOut eth_led2(p30);                //Ethernet LED2

//LCD Construtor
Serial display(LCD_TX, LCD_RX);       //LCD module initialization

//Timer Tickers 
Ticker start_convertion;            //Triggers ADC convertion
Timer t;                            //read ms timer to display and heartbeat functions

//There is a configuration file that can be accessed through usb in order to adjust current limits
LocalFileSystem local("local");     //Create the local filesystem under the name "local"

//Variable Initialization
EthernetInterface eth;              //ethernet interface
TCPSocketServer server;             //Socket
TCPSocketConnection client;         //Client

//Create the variables structure for 8 channels (index 0 to 7) 
channel CH[8];

//Create two spi global buffers - tx and rx
//LPC1768 SPI works in 12 bits. AMC7812B spi works in 24 bits.
union spi_buf{          //the same buffer may be expressed in two bytes (uint8_t) or a 16bit word (uint16_t)
    uint8_t byte[2];
    uint16_t data;
}rx,tx;

FILE *cfg_file;             //Config file variable
FILE *html_file;            //Html file variable
    
int tmp,i;                                  //auxiliary integers
int LCD_index=0;                            //LCD index (displayed channel changes from 1 to 8)
char LCD_buffer[40];                        //LCD char buffer    
char cfg=0;                                 //variable to identify config mode (config mode = current limit adjust mode)
float mult = 10;                            //multiplier used in config mode
char flag_adj=0;                            //flag to indicate adjust/config mode
//int r,c;                                    //auxiliary integers to LCD row and column       
uint8_t flag_end_u4,flag_end_u5;            //flags to indicate AD end of convertion
uint8_t register_amc;                       //8 bit variable to store AMC register to read/write
uint16_t reg_aux;                           //Aux 16 bit variable used in spi read/write functions

//global variables for socket message treatment
int ch_msg_idx,n;
char tmp_char;   
float ilimit_msg;

Thread main_thread,server_thread,socket_thread;         //threads instances
Thread display_thread;                                  //display thread (v3)
volatile uint8_t new_cfg;                               //new config to save to file
uint8_t display_status;                                        
uint8_t server_init=0,socket_init=0;                    //sockets init flags 

HttpServer httpsvr;         //http server instance
    
char socket_buffer[200];    //socket receive buffer
char send_buffer[100];      //socket send buffer

int receive_return;         //receive return for socket loop thread

char dev_name[30];          //device name string

//Display Functions
void error(void);                   //Turn Red Led - Error Status
void normal(void);                  //Turn Green Led - Normal Status     
void disabled(void);                //Turn (Red + Green) - Channel disabled
void write_LCD(void);               //Write to LCD Function
void reset_config(void);            //Reset config buttons status
void clear_lcd(void);               //Clear LCD Function
void heart(void);                   //Toggle function to heartbeat

//AMC7812B ADC and DAC functions
void fast_read_U5(void);    //U5 SPI fast read (returns previous transfer result)
void fast_read_U4(void);    //U4 SPI fast read (returns previous transfer result)
void read_U5(void);         //U5 SPI simple read - Takes two times the fast read
void read_U4(void);         //U4 SPI simple read - Takes two times the fast read
void write_U5(void);        //U5 SPI write
void write_U4(void);        //U4 SPI write

void setup(void);           //Initialization (display and variables)
void amc_setup(void);       //AMC7812B setup (U4 and U5)

//Config and Output functions
void adj_ilimit(int channel,float value);   //Adjust current limit
void fail_out(int channel,int value);       //Set/Reset failure output

//End of convertion interruptions
void end_conv_u4(void);         //U4 end of convertion function (interruption called)          
void end_conv_u5(void);         //U5 end of convertion function (interruption called)    

//ADC convertion Trigger Functions
void trigger_conv(void);        //Trigger convertion function (start U4 and U5 convertion) - Ticker called
void trigger_u4(void);          //Trigger U4 only (not used)
void trigger_u5(void);          //Trigger U5 only (not used)

extern "C" void mbed_reset();   //mbed reset function

void message_treatment(void);   //socket data received treatment

//Sockets functions
void socket_start();        //initiate socket instances
void server_loop(void);     //Server threaded loop - Port 80
void socket_loop(void);     //Socket threaded loop - Port 5757

//Thread loops
void main_loop(void);       //main thread (real-time priority) - deals with the AMC devices
void display_loop(void);    //display update thread - deals with buttons and LCD (uart)

volatile int save_cfg_time;

char html_page[HTML_MAX_SIZE];
int html_page_size;

//main function
int main() {    
    //heartbeat.attach(&heart,HEARTBEAT_DELAY);   //attach heartbeat ticker
    
    t.start();
    // Reading webserver file
    wait_ms(10);             //Wait powerup
    
    html_file = fopen("/local/index.htm","r");  // Open "index.html" on the local file system for reading
    if(html_file==NULL){  //If file doesnt exist
        html_page_size = sprintf(html_page,"ERROR: File Not Found");
        ERROR_PRINTF("index.htm file not found at local file system!");
        fclose(html_file);   //close html file
    }
    else
    {                 //File exists
        fseek(html_file, 0, SEEK_SET);  
        html_page_size = 0;
        while (!feof(html_file))
            html_page[html_page_size++]= getc(html_file);
        if(feof(html_file))
            html_page_size--;
        fclose(html_file);   //close html file
    }
    
    amc_setup();        //Setup AMC7812B
    setup();            //Initialize LCD and variables
    
    dav_u4.fall(&end_conv_u4);  //attach the address of the U4 end of convertion function to the falling edge
    dav_u5.fall(&end_conv_u5);  //attach the address of the U5 end of convertion function to the falling edge

    start_convertion.attach(&trigger_conv,CONV_DELAY);      //attach ticker to trigger convertions - USED ONLY IN DIRECT MODE
    //trigger_conv();                                       //Start convertion - USED ONLY IN AUTO MODE 
    
    if (0 == eth.init())//Use DHCP
    {
        eth.setName(dev_name);
        
    }
    else {
        clear_lcd();
        display.printf("HW ERROR: eth conn");   //HW ethernet error - probably PHY broken
        ERROR_PRINTF("Ethernet Connection Hardware Error!");
        //Turn LED 1 Red and Green - ETH HW Error
        display.putc(0xFE);
        display.putc(0x5A);
        display.putc(0x01);
        display.putc(0x03);
    }
    main_thread.set_priority(osPriorityRealtime);   //Start AMCs thread with high high high priority
    server_thread.set_priority(osPriorityNormal);   //HTTP server thread normal priority
    socket_thread.set_priority(osPriorityNormal);   //Socket server (EPICS) normal priority
    display_thread.set_priority(osPriorityNormal);   //Socket server (EPICS) normal priority

    display_thread.start(display_loop);   //Start AMC loop
    main_thread.start(main_loop);   //Start AMC loop
   
    while(1)
    {       
            if(new_cfg==1 && t.read_ms() - save_cfg_time >= SAVE_CFG_DELAY_MIN)  //File must be saved by the main thread. new_cfg is a flag indicating new data mus be saved
            {
                cfg_file = fopen("/local/lim.cfg", "w"); 
                               //Open "lim.cfg" on the local file system for writing
                fprintf(cfg_file,"Channel Configuration - Current Limit and Enable\n");      //Write header
                fprintf(cfg_file,"%s\n",dev_name);
                for(tmp=0;tmp<8;tmp++)
                {
                    fprintf(cfg_file,"%d %3.2f %d\n",tmp+1,CH[tmp].limit,CH[tmp].enable); //Write current limits to file
                }
                fclose(cfg_file);                                       //close file
                new_cfg=0; //reset flag
            }
            if(eth.is_connected())  //if eth connected
            {
                eth_led2 = !eth_led2;   //toggle led2
                eth_led1 = 1;           //turn on led1
                if(server_init == 0)    //initiate http server the first time
                {
                    httpsvr.init(html_page,html_page_size); 
                    server_init = 1;    //turn flag on - Server initiated
                    server_thread.start(server_loop);   //run thread - monitoring port 80
                }
                if(socket_init == 0)    //initiate socket server (PORT 5757) - EPICS
                {
                    socket_start();     
                    socket_init = 1;    //turn flag on
                    socket_thread.start(socket_loop);   //run thread - monitoring port 5757
                }
            }                             
            else //connect ethernet
            {
                if(eth_led1)    //eth_led serves also as flag
                {
                    eth.disconnect();   //disconnect interface
                    eth_led1=0;         //turn leds off
                    eth_led2=0;
                    //Turn Display LED red
                    display.putc(0xFE);
                    display.putc(0x5A);
                    display.putc(0x01);
                    display.putc(0x02);
                }
                if (0 == eth.connect())   //once connected, display IP and hostname on LCD  
                {
                    //Turn Display LED green
                    display.putc(0xFE);
                    display.putc(0x5A);
                    display.putc(0x01);
                    display.putc(0x01);
                }
            }   
    }       
}

void display_loop(void)
{
    int display_time,heart_time;
    char blink_led,byte_read;
    display_time = t.read_ms();
    heart_time = t.read_ms();
    blink_led = 0x01;
    while(1)
    {
        if(t.read_ms()- display_time >= DISPLAY_DELAY*1000)
        {
            display_time = t.read_ms();
            display_status = 0;
            if(cfg == 0)
            {                                   //If not in config mode
                LCD_index = (LCD_index+1)%8;    //Update index
                write_LCD();                    //call write_LCD()
            }
        }
        if(t.read_ms()- heart_time >= HEARTBEAT_DELAY*1000)
        {
            heart_time = t.read_ms();
            heart();
            display.putc(0xFE);
            display.putc(0x5A);
            display.putc(0x02);
            display.putc(blink_led);
            blink_led = blink_led ^ 0x01;   
        } 
        if(display.readable()) {
            byte_read = display.getc();
            if(byte_read=='B')//UP SQUARE BUTTON
            {
                if(cfg==0)
                {
                    display_time = t.read_ms();
                    display_status=1;
                    clear_lcd();
                    if(eth_led1 == 1)
                        display.printf("%-16s%-16s",eth.getIPAddress(),eth.getName());
                    else
                        display.printf("ETH DISCONNECTED");
                }
                else
                {
                    reset_config();
                }
            }
            if(byte_read=='A')//UP BUTTON
            {
                display_status=0;
                if(cfg==0)  //If not in CONFIG mode
                {
                    display_time = t.read_ms();
                    clear_lcd();
                    LCD_index = (LCD_index+1)%8;                        //increment LCD index
                    write_LCD();                                        //write to LCD
                }
                else        //If CONFIG mode
                {
                    if(CH[LCD_index].limit+mult <= CURR_MAX_LIMIT)          //Verify higher current bound condition
                        CH[LCD_index].limit = CH[LCD_index].limit + mult;   //Use multiplier to increment current 
                    display.putc(0xFE);
                    display.putc(0x47);
                    display.putc(0xC);
                    display.putc(0x2);
                    display.printf("%3.2f",CH[LCD_index].limit);
                    display.putc(0xFE);
                    display.putc(0x47);
                    if(cfg==1) //if config = 1, blink first digit
                        display.putc(0xB+cfg);
                    else        //else, blink second or third digit (after dot)
                        display.putc(0xC+cfg);
                    display.putc(0x2);
                }

            }
            if(byte_read=='C')//DOWN BUTTON
            {
                display_status=0;
                if(cfg==0)  //If not in CONFIG mode
                {
                    display_time = t.read_ms();
                    clear_lcd();
                    LCD_index = (LCD_index-1);                          //decrement LCD index                      
                    if(LCD_index==-1)                                   //Verify lower bound                
                        LCD_index = 7;
                    write_LCD();                                        //write to LCD
                }
                else        //If CONFIG mode
                {
                    if(mult<=CH[LCD_index].limit)                           //Verify lower current bound condition
                        CH[LCD_index].limit = CH[LCD_index].limit - mult;   //Use multiplier to decrement current
                    display.putc(0xFE);
                    display.putc(0x47);
                    display.putc(0xC);
                    display.putc(0x2);
                    display.printf("%3.2f",CH[LCD_index].limit);
                    display.putc(0xFE);
                    display.putc(0x47);
                    if(cfg==1) //if config = 1, blink first digit
                        display.putc(0xB+cfg);
                    else        //else, blink second or third digit (after dot)
                        display.putc(0xC+cfg);
                    display.putc(0x2);
                }
            }
            if(byte_read=='D')//DOWN SQUARE BUTTON
            {
                if(cfg<4&&display_status==0)   //If CONFIG<4, go to next config
                {
                    led_cfg=1;                                              //turn config led on
                    cfg = cfg++;                                            //increment config variable
                    mult = mult/10;                                         //adjust multiplier (divided by 10 each config increment)
                    display.putc(0xFE);
                    display.putc(0x47);
                    if(cfg==1) //if config = 1, blink first digit
                        display.putc(0xB+cfg);
                    else        //else, blink second or third digit (after dot)
                        display.putc(0xC+cfg);
                    display.putc(0x2);
                    display.putc(0xFE);
                    display.putc(0x53);
                }
                if(cfg>=4)  //If config >=4, configuration end
                {
                    reset_config();
                }    
            }
        } 
    }
}

void reset_config(void)
{
    led_cfg=0;                                              //turn config led off
    cfg=0;                                                  //reset config variable    
    mult = 10;
                                                            //reset mult to standard value = 10
    adj_ilimit(LCD_index+1,CH[LCD_index].limit);            //adjust current limit
    
    new_cfg=1;
    save_cfg_time=t.read_ms();
    display.putc(0xFE);
    display.putc(0x54);
}

//message incoming treatment - Socket EPICS
void message_treatment(void)
{
    ch_msg_idx = socket_buffer[0] - 0x30;   //First character is the channel number
    if(ch_msg_idx>0 && ch_msg_idx<=8)       //drop if out of range
    {
        ch_msg_idx--;   //decrement to index 0 to 7
        if(socket_buffer[1]=='r')   //read message
        {
            n = sprintf(send_buffer,"OK>%d:\tV=%4.2f\tI=%3.2f\tC=%.1f\tIL=%3.2f\tF=%d\tOL=%d\tNL=%d\tE=%d\r\n",ch_msg_idx+1,\
                    CH[ch_msg_idx].voltage,CH[ch_msg_idx].current,\
                    CH[ch_msg_idx].control,CH[ch_msg_idx].limit,\
                    CH[ch_msg_idx].failure,CH[ch_msg_idx].overload>ERROR_REP,\
                    CH[ch_msg_idx].noload>ERROR_REP,CH[ch_msg_idx].enable);
            return;
        }
        if(socket_buffer[1]=='l') //current limit message
        {
            if(socket_buffer[2]=='?')   //get
            {
                n = sprintf(send_buffer,"OK>%d:\tIL=%3.2f\r\n",ch_msg_idx+1,CH[ch_msg_idx].limit);   
                return;
            }
            if(socket_buffer[3]=='.')   //set (must be in 3.2f format)
            {
                ilimit_msg = (socket_buffer[2]-0x30) + (socket_buffer[4]-0x30)/10.0 + (socket_buffer[5]-0x30)/100.0;    //convert three digits to float
                if(ilimit_msg > 0 && ilimit_msg <= CURR_MAX_LIMIT)  //verifiy bounds
                {
                    CH[ch_msg_idx].limit = ilimit_msg;
                    ilimit_msg = ilimit_msg*I_K+I_OFF;              //Convert to voltage
                    do{
                        tx.data = int((0xFFF*ilimit_msg)/5)&0xFFF;      //Convert to 12 bit data
                        register_amc=DAC0+ch_msg_idx;                   //set register
                        write_U4();
                        read_U4();
                    }while(tx.data!=rx.data);   //try to write until success. (main thread also using SPI)
                    
                    n = sprintf(send_buffer,"OK>%d:\tIL=%3.2f\r\n",ch_msg_idx+1,CH[ch_msg_idx].limit);  
                    new_cfg=1;  //new cfg file must be written by the main loop
                    save_cfg_time = t.read_ms();
                    return;
                }
                else
                {
                    n = sprintf(send_buffer,"ERROR>%5.2f out of range\r\n",ilimit_msg);   
                    return;
                }
            }
        }
        if(socket_buffer[1]=='e') //enable message
        {
            if(socket_buffer[2]=='?')   //get method
            {
                n = sprintf(send_buffer,"OK>%d:\tE=%d\r\n",ch_msg_idx+1,CH[ch_msg_idx].enable);   
                return;
            }
            if(socket_buffer[2]=='1')   //set method (enable=1)
            {
                do{
                register_amc = GPIO;                        //set GPIO register
                read_U5();                                  //read register
                tx.data = rx.data | (0b1<<ch_msg_idx);
                write_U5();                                 //write the 1 position
                read_U5();
                }while(tx.data != rx.data);     //try to write until success. (main thread also using SPI)                           
                CH[ch_msg_idx].enable = 1;
                new_cfg=1;
                save_cfg_time=t.read_ms();
                n = sprintf(send_buffer,"OK>%d:\tE=%d\r\n",ch_msg_idx+1,CH[ch_msg_idx].enable);   
                return;
            }
            if(socket_buffer[2]=='0')    //set method (enable=0) - Disable
            {
                do{
                    register_amc = GPIO;                        //set GPIO register
                    read_U5();                                  //read register
                    tx.data = rx.data & ~(0b1<<ch_msg_idx);
                    write_U5();                                 //write the 0 position
                    read_U5();                                 
                }while(tx.data != rx.data);     //try to write until success. (main thread also using SPI)
                CH[ch_msg_idx].enable = 0;
                new_cfg=1;
                save_cfg_time=t.read_ms();
                n = sprintf(send_buffer,"OK>%d:\tE=%d\r\n",ch_msg_idx+1,CH[ch_msg_idx].enable);   
                return;
            }
        }
    }
    if(socket_buffer[0]=='p'&&socket_buffer[1]=='i'&&socket_buffer[2]=='n'&&socket_buffer[3]=='g')  //ping message
    {
        n = sprintf(send_buffer,"OK>pong\r\n"); //answer pong
        return;    
    }
    if(socket_buffer[0]=='v')   //firmware version
    {
        n = sprintf(send_buffer,"OK>firmware version = %s\r\n",VERSION);
        return;    
    }
    if(socket_buffer[0]=='n')   //device name
    {
        if(socket_buffer[1]=='?')
        {
            n = sprintf(send_buffer,"OK>device name = %s\r\n",dev_name);
            return;    
        }
        if(socket_buffer[1]=='=')
        {
            n=0;
            while(socket_buffer[n+2]!='\0'&&n<=20)
            {
                dev_name[n]=socket_buffer[n+2];
                n++;   
            }
            dev_name[n]='\0';
            new_cfg=1;
            save_cfg_time=t.read_ms();
            n = sprintf(send_buffer,"OK>device name = %s\r\n",dev_name);
            return;    
        }
        
    }
    if(socket_buffer[0]=='r'&&socket_buffer[1]=='e'&&socket_buffer[2]=='s'&&socket_buffer[3]=='e'&&socket_buffer[4]=='t')   //reset messag
    {
        n = sprintf(send_buffer,"OK>rebooting...\r\n"); //answer
        client.send_all(send_buffer,n);
        wait_ms(500);
        mbed_reset();   //reset function
        return;    
    }     
    n = sprintf(send_buffer,"ERROR> NOT-A-CMD\r\n\0");  //command not found
}

void main_loop(void) //main thread (real-time priority) - read and write AMC devices
{
    while(1){
        
        if(flag_end_u5)     //Wait U5 end of convertion
        {
            flag_end_u5 = 0;    //Reset flag
            
            register_amc = ADC0;    //reads U5 ADC0
            fast_read_U5();
            for(i=0;i<=7;i++)
            {
                register_amc = ADC0+i+1;    //reads U5 ADC1 + i
                fast_read_U5();
                
                CH[i].current = FILTC*CH[i].current + FILTC2*(((rx.data*5.0)/0xFFF)-I_OFF)/I_K; //store current of the previous channel
                if(CH[i].current<0) //negative current not allowed
                    CH[i].current=0;
            }
            
        }
        
        if(flag_end_u4)     //Wait U4 end of convertion
        {   
            flag_end_u4 = 0;    //Reset flag 
                  
            register_amc = ADC0;    //reads U4 ADC0
            fast_read_U4();
            for(i=0;i<=7;i++)
            {
                register_amc = ADC8+i;     //reads U4 ADC8 + i (ADC8 to ADC15)
                fast_read_U4();
                
                CH[i].voltage = FILTC*CH[i].voltage+FILTC2*((rx.data*5.0)/0xFFF)/V_K; //store voltages (previous convertions - ADC0 to ADC7)
                if(CH[i].voltage<0) //negative voltage not allowed
                    CH[i].voltage = 0;
                
                //Failure output triggered by NO LOAD or OVERLOAD
                //Determine overload via software (above 90% current limit)
                if(CH[i].current > CH[i].limit*0.85)
                {
                    if(CH[i].overload<=2*ERROR_REP) //verify counter limit
                        CH[i].overload++;           //Increment overload counter    
                    if(CH[i].overload>=ERROR_REP)   //Digital Filtering by repetitive overload diagnostics
                        fail_out(i+1,1);            //Set failure output
                }
                else
                {
                    if(CH[i].overload>0)
                        CH[i].overload--;           //decrement overload counter
                    if(CH[i].overload<ERROR_REP)    //verify overload condition           
                        if(CH[i].noload<ERROR_REP)  //verify no load condition
                            fail_out(i+1,0);        //Reset failure output      
                    
                }
                //Determine no load via software (Resistance over 150 ohm)
                if(CH[i].voltage > 2 && CH[i].current < CH[i].voltage/150)
                {                    
                    if(CH[i].noload<=2*ERROR_REP)   //verify counter limit
                        CH[i].noload++;             //Increment no load counter 
                    if(CH[i].noload>=ERROR_REP)     //Digital Filtering by repetitive no load diagnostics
                        fail_out(i+1,1);            //Set failure output
                }
                else
                {
                    if(CH[i].noload>0)          
                        CH[i].noload--;                 //decrement no load counter
                    if(CH[i].noload<ERROR_REP)          //verify no load condition
                        if(CH[i].overload<ERROR_REP)    //verify overload condition 
                            fail_out(i+1,0);            //Reset failure output 
                }
                
                register_amc = ADC0+i+1;                //Read next ADC (ADC0 to ADC7)
                fast_read_U4();
                
                //Estimate control input (Duty cycle in %)
                CH[i].control = FILTC*CH[i].control+FILTC2*(rx.data*VC_K*5.0/0xFFF+VC_OFF);
                
                #ifndef CAL
                    //Verify limits
                    if(CH[i].control<0)
                        CH[i].control=0;
                    if(CH[i].control>100)
                        CH[i].control=100;  
                #endif                   
            }
            DEBUG_PRINTF("Duty Cycle Calibration - Measured Voltage: %f",rx.data*5.0/0xFFF);
        } 
    }          
}

//initiate socket instances
void socket_start()
{
    server.set_blocking(true); // Set socket connection not blocking
    client.set_blocking(false,50);
    server.bind(PORT); //  TCP Socket setup
    server.listen(2); //  Socket Server start listening request. 
}


//Server loop (thread triggered)
void server_loop(void)
{
    while(1)
    {
        httpsvr.run(&CH[0],dev_name);        //wait for new client connection 
        server_led = !server_led;   //togle server led    
    }
}

//Socket loop (thread triggered) - for EPICS
void socket_loop(void)
{
    
    while(1)
    {
        socket_led = !socket_led;   //toggle socket LED
                
        if(server.accept(client)!=-1)   //blocks waiting for a new client
        {
            client.set_blocking(false,50);  // Set client connection not blocking
            while(client.is_connected())    // while connected
            {
                socket_led = !socket_led;   //toggle LED again (fast flash)
                receive_return = client.receive_all(socket_buffer,200); //receive all messages
                if(receive_return>0)    //if bytes received
                {
                    message_treatment();            //treat message
                    client.send_all(send_buffer,n); //send answer
                }
                if(receive_return < 0)              //if erro, break connection
                    break;
            }
            client.close(); //finally close the client
        }       
    }
}

//AMC7812B setup (U4 and U5)
void amc_setup(void){
    
    cs_u4 = 1;      //Chip Select must be deselected
    cs_u5 = 1;      //Chip Select must be deselected
    dac_clr = 1;    //DAC Clear inactive
    amc_reset = 1;  //Reset Inactive
    cnvt_u5 = 1;    //U5 Convertion not triggered                     
    cnvt_u4 = 1;    //U4 Convertion not triggered
    
    //Reset AMC7812B to make sure both are in the initial state
    amc_reset = 0;
    wait_ms(5);
    amc_reset = 1;
    
    // Setup the spi for 12 bit data, high steady state clock,
    // second edge capture, with a 10MHz clock rate
    spi.format(12,1);
    spi.frequency(10000000);
    
    wait_ms(10);             //Wait AMC7812B power up
    
    //Disabling Power Down Register
    tx.data = 0x7FFE;
    register_amc = PWR_DOWN;
    write_U4();
    write_U5();
    
    //Set all ADC range 0 to 5V
    tx.data = 0xFFFF;
    register_amc = ADC_GAIN;
    write_U4();
    write_U5();

    //Set all u4 16 ADC channels active
    register_amc = ADC_CH0;
    tx.data = 0x6DFF;
    write_U4();
    register_amc = ADC_CH1;
    tx.data = 0x7000;
    write_U4();
    
    //Set u5 first 8 ADC channels active
    tx.data = 0x6DE0;
    register_amc = ADC_CH0;
    write_U5();
    
    //Disable LT, D1 and D2
    tx.data = 0x0000;
    register_amc = T_CONF;
    write_U4();
    write_U5();
    
    tx.data = 0x0400; //Set both in direct mode
    //tx.data = 0x2400; //Set both in auto mode
    register_amc = CONF0;
    write_U4();
    write_U5();

    tx.data = 0x0300; //Set convertion ate to 62.5kS/s (auto mode)
    register_amc = CONF1;
    write_U4();
    write_U5();
    
}

//Adjust current limit
//
// Parameters:
// - int channel: channel to adjust (from 1 to 8)
// - float value: current value
//
void adj_ilimit(int channel,float value) //channel from 1 to 8
{
    if(value > CURR_MAX_LIMIT) //current allowed up to CURR_MAX_LIMIT
        value = CURR_MAX_LIMIT;
        
    value = value*I_K+I_OFF;    //Convert to voltage
    
    tx.data = int((0xFFF*value)/5)&0xFFF;   //Convert to 12 bit data
    register_amc=DAC0+channel-1;            //set register
    write_U4();                             //write U4 AMC7812B DAC
}

//Set/Reset Failure Output
//
// Parameters:
// - int channel: channel to adjust (from 1 to 8)
// - int value: output 5V (value=1) or 0V (value=0)
//
void fail_out(int channel,int value)//channel from 1 to 8
{
    tx.data = 0xFFF*value;          //set 12bit data to DAC
    register_amc=DAC0+channel-1;    //set register
    write_U5();                     //write U5 AMC7812B
    CH[channel-1].failure = value;  //update data structure
}

//U4 end of convertion Interrupt 
void end_conv_u4(void){
    flag_end_u4 = 1;        //set U4 flag
}

//U5 end of convertion Interrupt 
void end_conv_u5(void){
    flag_end_u5 = 1;        //set U5 flag
} 

//Trigger Both (U4 and U5) AD convertions
void trigger_conv(void){
    
    //Triggering ADC via software - Setting flag ICONV inside CONF register
    /*
    register_amc = CONF0;
    read_U4();
    tx.data = rx.data | 0x1000;
    write_U4();
    
    read_U5();
    tx.data = rx.data | 0x1000;
    write_U5();
    */
    
    //Triggering ADC via hardware - CONV pin active low.
    cnvt_u5 = 0;
    cnvt_u4 = 0;
    
    cnvt_u5 = 1;
    cnvt_u4 = 1;      
     
}

//Trigger U4 convertion
void trigger_u4(void)
{
    cnvt_u4 = 0;
    cnvt_u4 = 1;
}

//Trigger U5 convertion
void trigger_u5(void)
{
    cnvt_u5 = 0;
    cnvt_u5 = 1;
}

//U5 Fast Read Function
//fast_read saves always the previous read value in rx.data
//See AMC7812B datasheet for more details
void fast_read_U5(void){
        cs_u5 = 0; // Select the device by seting chip select low

        reg_aux = spi.write((register_amc|0x80)<<4);
        rx.data = (reg_aux&0xF)<<12;
        reg_aux = spi.write(0x000);
        rx.data = rx.data|(reg_aux&0xFFF);
        
        cs_u5 = 1; // Deselect the device by seting chip select low 
}

//U4 Fast Read Function
//fast_read saves always the previous read value in rx.data
//See AMC7812B datasheet for more details
void fast_read_U4(void){
        cs_u4 = 0; // Select the device by seting chip select low

        reg_aux = spi.write((register_amc|0x80)<<4);
        rx.data = (reg_aux&0xF)<<12;
        reg_aux = spi.write(0x000);
        rx.data = rx.data|(reg_aux&0xFFF);
                 
        cs_u4 = 1; // Deselect the device by seting chip select low 
}

//U5 SPI read function
//Read two times to assure the second read will save the first read result in rx.data
//Takes about two times the fast_read function
void read_U5(void){
        cs_u5 = 0; // Select the device by seting chip select low

        spi.write((register_amc|0x80)<<4);
        spi.write(0x000);
        cs_u5 = 1; // Deselect the device by seting chip select low
        cs_u5 = 0; // Select the device by seting chip select low
        reg_aux = spi.write((register_amc|0x80)<<4);
        rx.data = (reg_aux&0xF)<<12;
        reg_aux = spi.write(0x000);
        rx.data = rx.data|(reg_aux&0xFFF);
        
        cs_u5 = 1; // Deselect the device by seting chip select low 
}

//U4 SPI read function
//Read two times to assure the second read will save the first read result in rx.data
//Takes about two times the fast_read function
void read_U4(void){
        cs_u4 = 0; // Select the device by seting chip select low

        spi.write((register_amc|0x80)<<4);
        spi.write(0x000);
        cs_u4 = 1; // Deselect the device by seting chip select low
        cs_u4 = 0; // Select the device by seting chip select low
        reg_aux = spi.write((register_amc|0x80)<<4);
        rx.data = (reg_aux&0xF)<<12;
        reg_aux = spi.write(0x000);
        rx.data = rx.data|(reg_aux&0xFFF);
        
        cs_u4 = 1; // Deselect the device by seting chip select low 
}

//U4 SPI write function
void write_U4(void){
        
        cs_u4 = 0; // Select the device by seting chip select low
        reg_aux = register_amc<<4|((tx.data&0xF000)>>12);
        spi.write(reg_aux);
        spi.write(tx.data&0xFFF);
        cs_u4 = 1; // Deselect the device by seting chip select low 
}

//U5 SPI write function
void write_U5(void){
        cs_u5 = 0; // Select the device by seting chip select low
        reg_aux = register_amc<<4|((tx.data&0xF000)>>12);
        spi.write(reg_aux);
        spi.write(tx.data&0xFFF);
        cs_u5 = 1; // Deselect the device by seting chip select low 
}

//Initialize variables and LCD
void setup(){
    
    //Auxiliary variables    
    int aux;
    char c;
    float tmp_lim;
    int tmp_en;
    uint8_t gpio_init=0;
    
    phy_rst = 0;    //reset ethernet Phy
    eth_led1 = 0;   //turn off ethernet led 1
    eth_led2 = 0;   //turn off ethernet led 2
    phy_rst = 1;    //Power on ethernet Phy
    
    display.baud(19200);
    
    //Clear all display LEDs
    display.putc(0xFE);
    display.putc(0x5A);
    display.putc(0x00);
    display.putc(0x00);
    display.putc(0xFE);
    display.putc(0x5A);
    display.putc(0x01);
    display.putc(0x00);
    display.putc(0xFE);
    display.putc(0x5A);
    display.putc(0x02);
    display.putc(0x00);
       
    //Set display contrast
    display.putc(0xFE);
    display.putc(0x91);
    display.putc(0x80);
    
    //Config file handler
    cfg_file = fopen("/local/lim.cfg", "r");  // Open "lim.cfg" on the local file system for reading
    if(cfg_file==NULL)  //If config file doesnt exist, create new file
    {
        cfg_file = fopen("/local/lim.cfg", "w");  //Open "lim.cfg" on the local file system for writing
        fprintf(cfg_file,"Channel Configuration - Current Limit and Enable\n");
        fprintf(cfg_file,"%s\n",DEVICE_NAME);
        strcpy(dev_name,DEVICE_NAME);
        for(i=0;i<8;i++)
        {
            CH[i].limit = DEFAULT_CURR_LIMIT;    //Save default current limit in all channels          
            fprintf(cfg_file,"%d %3.2f 1\n",i+1,DEFAULT_CURR_LIMIT);  //Write to file
            CH[i].enable = 1;
        }
        ERROR_PRINTF("lim.cfg file not found! Creating default file...");
    }
    else                //File exists :)
    {
        do{
            c = getc(cfg_file);
        }while(c!='\n');    //Linescan (scan until \n)
        fscanf(cfg_file,"%s",dev_name); //Read name string
        for(i=0;i<8;i++)    //Read 8 channels
        {
            fscanf(cfg_file,"%d %f %d",&aux,&tmp_lim,&tmp_en); //Read channel and limit value
            if(tmp_lim<0||tmp_lim>CURR_MAX_LIMIT)   //Verify limits
                tmp_lim=DEFAULT_CURR_LIMIT;         //If tmp is above limit, use default  
            CH[i].limit = tmp_lim;  //set channel limit
            CH[i].enable = tmp_en&0x1;
        }   
    }
    fclose(cfg_file);   //close config file
    gpio_init=0;
    for(i=0;i<8;i++)    //set structure variables
    {
        CH[i].noload = 0;   //no load variable
        CH[i].overload = 0; //oveload variable     
        fail_out(i+1,0);    //reset failure output
        adj_ilimit(i+1,CH[i].limit);    //adjust current limit 
        gpio_init = gpio_init | (CH[i].enable&0x1)<<i;
    }
        
    write_LCD();        //write to LCD
      
    reset_config();     //reset configuration
    flag_end_u4=0;      //reset U4 end of convertion flag
    flag_end_u5=0;      //reset U5 end of convertion flag
    
    //set GPIO aoutput high (enable)
    tx.data = gpio_init;
    //enable all channels (hardware GPIO)
    register_amc = GPIO;
    write_U5();
    
}

//Clear LCD Function
void clear_lcd(){
    display.putc(0xFE);
    display.putc(0x58);
}

//Set Red Led - Error
void error(){
    display.putc(0xFE);
    display.putc(0x5A);
    display.putc(0x00);
    display.putc(0x02);
}

//Set Green Led - Normal
void normal(){
    display.putc(0xFE);
    display.putc(0x5A);
    display.putc(0x00);
    display.putc(0x01);
}

//Set Red and Green - Disabled
void disabled(){
    display.putc(0xFE);
    display.putc(0x5A);
    display.putc(0x00);
    display.putc(0x03);
}

//Write channel index to LCD
void write_LCD()
{
    if(CH[LCD_index].failure == 1)      
        error();                          //error status if failure
    else
    {
        if(CH[LCD_index].enable == 0)
            disabled();                   //disabled status if not enabled
        else
            normal();                     //Normal mode
    }
    clear_lcd();                          //Clear LCD
    display.printf("CH%d %5.2fV/%3.2fAC:%03.0f%%  IL:%3.2fA",LCD_index+1,CH[LCD_index].voltage,CH[LCD_index].current,CH[LCD_index].control,CH[LCD_index].limit);
}

//Heartbeat toogle MBED led function 
void heart(){
    blink = !blink; //toggle blink led
}