/*
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                                                                                                 ┃
┃          Program Name: RiscV_Phasenmessung.c                                                    ┃
┃                Author: Michael Krämer                                                           ┃
┃                 Email: Michael@dk8pp.de                                                         ┃
┃                  Date: 01.08.2025                                                               ┃
┃                  Tool: GMD V2025.01                                                             ┃
┃                Device: GW5A-LV25MG121 Typ A                                                     ┃
┃               Version: 0.1                                                                      ┃
┃        Program status: still testing                                                            ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  Purpose: First tests for phase measurement.                                                    ┃
┃                                                                                                 ┃
┃                                                                                                 ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
┃                                                                                                 ┃
┃  © Copyright 2025-2026 Michael Krämer, DK8PP                                                    ┃
┃                                                                                                 ┃
┃  This program is free software: you can redistribute it and/or modify it under the terms of     ┃
┃  the GNU Lesser General Public License as published by the Free Software Foundation, either     ┃
┃  version 3 of the License, or (at your option) any later version.                               ┃
┃                                                                                                 ┃
┃  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;      ┃
┃  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.      ┃
┃  See the GNU Lesser General Public License for more details.                                    ┃
┃                                                                                                 ┃
┃  You should have received a copy of the GNU Lesser General Public License along with this       ┃
┃  program.   If not, see <http://www.gnu.org/licenses/>.                                         ┃
┃                                                                                                 ┃
┃  ============================================================================================   ┃
┃                                                                                                 ┃
┃  Dieses Programm ist Freie Software: Sie können es unter den Bedingungen der GNU Lesser         ┃
┃  General Public License, wie von der Free Software Foundation, Version 3 der Lizenz oder        ┃
┃  (nach Ihrer Wahl) jeder neueren veröffentlichten Version, weiter verteilen und/oder            ┃
┃  modifizieren.                                                                                  ┃
┃                                                                                                 ┃
┃  Dieses Programm wird in der Hoffnung bereitgestellt, dass es nützlich sein wird, jedoch OHNE   ┃
┃  JEDE GEWÄHR,; sogar ohne die implizite Gewähr der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN        ┃
┃  BESTIMMTEN ZWECK. Siehe die GNU Lesser General Public License für weitere Einzelheiten.        ┃
┃                                                                                                 ┃
┃  Sie sollten eine Kopie der GNU Lesser General Public License zusammen mit diesem Programm      ┃
┃  erhalten haben. Wenn nicht, siehe <https://www.gnu.org/licenses/>.                             ┃
┃                                                                                                 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Principles of operation:

This program reads the results from the phase comparator in the FPGA and from the AD converter,
 processes them and transmits them regularly via the serial interface.

A timer interrupt is generated in 10 ms intervals. The interrupt handler calls the "read_adc"
 function to read the result from the previous conversion and to start the conversion for the next
 channel. The output voltages of the logarithmic amplifiers are read more often than the operating
 voltages and the temperature. All results are stored in global variables and any previous values
 are overwritten. Note that this causes a skew of 10 ms between the ADC channels. The clock speed
 of the ADC SPI-interface is set to 1 MHz. A single transfer takes a little more than 32 µs and it
 is executed in the interrupt handler.

Transmission of the ADC results is triggered after <TRANSMIT_INTERVAL> timer interrupts.
 Transmission is triggered by the timer interrupt handler and proceeds automatically by the UART
 TX-interrupt.

Transmission of all results, i.e. ADC and reflection measurement, is executed when reflection
 measurement has completed.

********************************** important precaution (start) **********************************

                                    ==== hic sunt leones ====

    There is a problem with interrupts occuring at the start of the sprintf function (probably
    including all its derivatives like printf). As a result of that problem, instead of the
    desired data, the string "33554766" is inserted into the output string. This may vary under
    other conditions, but it is repeatable here and can be used as a trigger.

    The described problem seems to be independent of the actions performed in the interrupt
    handler, i.e. the acknowledged interrupt including start and end of the interrupt handler is
    enough.

    The problem seems to be caused by the handling of the va_list. It may also well be, that the
    interrupt handler does not properly safe and restore all significant registers, but I did not
    dig deeper.

    As a workaround, global interrupts are disabled in this program, before sprintf is called. To
    keep interrupt disable times short, global interrupts are unconditionally enabled in the
    sprintf function, immediately after va_start(). This seems to fix the problem.

    Take care to adapt the code, if interrupts must be kept disabled.

    Michael@dk8pp.de, 07.01.2026

********************************** important precaution (end) ************************************


Notes:
======
Using RISC-V at 10 MHz CPU clock

Note the reduced printf functionality:

int main(void)
{
    char *ptr = "Hello world!";
    char *np = 0;
    int i = 5;
    unsigned int bs = sizeof(int)*8;
    int mi;
    char buf[80];

    mi = (1 << (bs-1)) + 1;
    printf("%s\n", ptr);
    printf("printf test\n");
    printf("%s is null pointer\n", np);
    printf("%d = 5\n", i);
    printf("%d = - max int\n", mi);
    printf("char %c = 'a'\n", 'a');
    printf("hex %x = ff\n", 0xff);
    printf("hex %02x = 00\n", 0);
    printf("signed %d = unsigned %u = hex %x\n", -3, -3, -3);
    printf("%d %s(s)%", 0, "message");
    printf("\n");
    printf("%d %s(s) with %%\n", 0, "message");
    sprintf(buf, "justif: \"%-10s\"\n", "left"); printf("%s", buf);
    sprintf(buf, "justif: \"%10s\"\n", "right"); printf("%s", buf);
    sprintf(buf, " 3: %04d zero padded\n", 3); printf("%s", buf);
    sprintf(buf, " 3: %-4d left justif.\n", 3); printf("%s", buf);
    sprintf(buf, " 3: %4d right justif.\n", 3); printf("%s", buf);
    sprintf(buf, "-3: %04d zero padded\n", -3); printf("%s", buf);
    sprintf(buf, "-3: %-4d left justif.\n", -3); printf("%s", buf);
    sprintf(buf, "-3: %4d right justif.\n", -3); printf("%s", buf);

    return 0;
}

 * if you compile this file with
 *   gcc -Wall $(YOUR_C_OPTIONS) -DTEST_PRINTF -c printf.c
 * you will get a normal warning:
 *   printf.c:214: warning: spurious trailing `%' in format
 * this line is testing an invalid % at the end of the format string.
 *
 * this should display (on 32bit int machine) :
 *
 * Hello world!
 * printf test
 * (null) is null pointer
 * 5 = 5
 * -2147483647 = - max int
 * char a = 'a'
 * hex ff = ff
 * hex 00 = 00
 * signed -3 = unsigned 4294967293 = hex fffffffd
 * 0 message(s)
 * 0 message(s) with %
 * justif: "left      "
 * justif: "     right"
 *  3: 0003 zero padded
 *  3: 3    left justif.
 *  3:    3 right justif.
 * -3: -003 zero padded
 * -3: -3   left justif.
 * -3:   -3 right justif.

ToDo:
=====

*/

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Includes                                                                                  │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
//#include <stdio.h>
#include <stdlib.h>
//#include <stddef.h>
//#include <stdbool.h>
//#include "gw1ns4c.h"
#include "config.h"
#include "picorv32.h"
#include "firmware.h"
#include "irq.h"
#include "simpleuart.h"
#include "wbuart.h"
#include "wbgpio.h"
#include "wbspi.h"
#include "wbspiflash.h"
#include "project.h"

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Defines                                                                                   │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
#define DEV_GW5A25_15

//#define TEST_TEMPERATURE_DECODING /* to test temperature decoding */
//#define SHOW_ADC_RESULTS        /* transmit results from ADC every 100 ms */
#define SHOW_CONFIG             /* show configuration */
//#define SHOW_PHASE_MEASUREMENT  /* show raw phase measurement results */
#define SHOW_ALL_RESULTS        /* show all measurment results */
//#define SHOW_ALL_SIMPLIFIED     /* show all measurment results, simplified, constant length */

#define TRANSMIT_INTERVAL 10    /* transmit results once every 10 timer interrupts = once per 100 ms */
#define TX_BUFFER_SIZE 256      /* size of transmit buffers */

//#define IT_RELOAD 399999        /* reload value for interval timer, 10 ms interval at 40 MHz CPU clock */
#define IT_INTERVAL_MS 10       /* timer interval length in ms */
//#define RMI_MS 25               /* reflection measurement interval time in ms */
#define RM_CLKS 5000000         /* number of sampling clocks per measurement interval, must be an integral multiple of 1000 */

/* UART */
#ifdef USE_WBUART
#define get_char()   wbuart_getc()
#define put_char(c)  wbuart_putc(c)
#else
#define get_char()   uart_getchar()
#define put_char(c)  uart_putchar(c)
#endif

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       structs and enums                                                                         │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
typedef struct txbuffer_struct {    /* transmit buffer */
        char *buffer ;              /* address of buffer */
        uint16_t size ;             /* size of buffer */
        volatile uint16_t next ;    /* next element in buffer */
        volatile bool completed ;   /* true if transmission is completed */
        } TXBUFFER ;

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Function prototypes                                                                       │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void timer_int() ;
void user0_int() ;
int32_t make_ads1118_temperature(uint16_t raw, uint16_t dp) ;
void read_adc_raw() ;
void atx_send(TXBUFFER *tx) ;
void user0_int() ;
void user1_int() ;
bool wait_for_buffer_to(TXBUFFER *tx, uint32_t timeout) ;
void test_tempdecode() ;
void task_10ms() ;

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       global variables                                                                          │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
bool volatile flag_10ms ;           /* request execution of 10 ms tasks */
volatile uint32_t wait_to ;         /* timeout counter for "wait for buffer" */
static volatile uint32_t adc_rawresults[5] ; /* ADC raw results */
static volatile int32_t adc_temperature ;    /* temperature in m°C */

static volatile int adcraw_sm = ADC_IDLE ;  /* init ADC raw state machine */

static volatile uint16_t tx_interval ;      /* counter for transmit interval of ADC results */
static volatile bool trigger_tx = false ;   /* set when transmission of ADC results is to be executed */

char txbuffer[TX_BUFFER_SIZE] ;

char const CompileDate[] = __DATE__ ;    /* Mmm dd yyyy */
char const CompileTime[] = __TIME__ ;    /* hh:mm:ss */

TXBUFFER mybuff = {.buffer = txbuffer,
                   .size = TX_BUFFER_SIZE,
                   .next = 0,
                   .completed = true} ;

TXBUFFER *atx_currentbuffer = NULL ;

uint32_t f_leads_r ;
uint32_t r_leads_f ;
uint32_t status ;
uint32_t phase_100 ;    /* Phase Difference in 1/100 degrees */
uint32_t f_quality ;    /* "quality" of forward signal */
uint32_t r_quality ;    /* "quality" of reflected signal */
uint32_t f_khz ;        /* forward frequency in kHz */
uint32_t r_khz ;        /* reflected frequency in kHz */
uint16_t index ;        /* loop counter */
char *shift ;
uint32_t phasediff ;
uint32_t tmp_ui ;       /* temporary register */
uint32_t tmp_uj ;       /* temporary register */
uint32_t it_ms ;        /*  interval time in ms */

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Inline functions                                                                          │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/

/* AIN0: VIN supply via 15k-1k = VIN * 1/16, ==> LSB=125µV*16 = 2mV, round to next mV */
__inline static uint32_t adc0() {return(2ul * adc_rawresults[0]) ; }

/* AIN1: 3,3V supply, LSB=125µV, round to next mV */
__inline static uint32_t adc1() {return((125ul * adc_rawresults[1]) + 500ul) / 1000ul ; }

/* AIN2: LOGUF, LSB=125µV, round to next mV */
__inline static uint32_t adc2() {return((125ul * adc_rawresults[2]) + 500ul) / 1000ul ; }

/* AIN3: LOGUR, LSB=125µV, round to next mV */
__inline static uint32_t adc3() {return((125ul * adc_rawresults[3]) + 500ul) / 1000ul ; }

/* Temperature */
__inline static uint32_t adct() {return(make_ads1118_temperature(adc_rawresults[4] >> 2, 3)) ; }

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       Main                                                                                      │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ MAIN program.                                                                                   │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
//int main(void)
int __attribute__((optimize("O0"))) main(void)

/* !!! fails with optimization, reason to be checked !!! */

{

    tx_interval = TRANSMIT_INTERVAL ; /* next transmission of ADC results */

    for (index=0; index<5; index++) adc_rawresults[index] = 0ul ;

    flag_10ms = false ;

/* Disable all interrupts */
    mask_irq(0xffffffff);

/* init UART baudrate */
    PICO_WBUART->SETUP = (*SI_CPUFREQ + 115200/2) / 115200 ;
    *AT_BRGRLD = (*SI_CPUFREQ + 115200/2) / 115200 ;

//    irq_enable_one_bit(INTNUM_TIMER);                   /* 32-bit timer interrupt */
    irq_enable_one_bit(INTNUM_MISALIGNED_INSTRUCTION);  /* Misalignment access error of memory (bus error) */
    irq_enable_one_bit(INTNUM_ILLEGAL_INSTRUCTION);     /* Illegal instruction error */
//    irq_enable_one_bit(INTNUM_WB_UART);                 /* Wishbone UART interrupt */
    irq_enable_one_bit(INTNUM_USER0);                   /* User0 interrupt, transmit buffer empty */
    irq_enable_one_bit(INTNUM_USER1);                   /* User1 interrupt, interval timer */

    enable_external_interrupt();        /* Enable external interrupts */

//    set_timer(*cpufreq/100) ;   /* 100 interrupts per sec. Set the timer a non-zero value, this will start the timer automatically. */
//    enable_timer_interrupt();           /* Enable timer interrupt */

    enable_interrupt_global();          /* Enable global interrupt */

/* Note: all frequencies and clock counts must be an integral multiple of 1000 */

/* setup interval timer to generate regular interrupts */
    *T1_RLD = ((*SI_CPUFREQ * IT_INTERVAL_MS) / 1000) - 1 ;
    *T1_CONFIG = 0x1 ;                  /* start interval timer, configuration bit0=enable */

/* setup reflection measurement */
    *RM_INTERVAL = RM_CLKS - 1 ;        /* set reflection measurement interval count */
    it_ms = RM_CLKS / (*SI_SAMPLINGFREQ / 1000) ; /*  interval time in ms */
    *RM_CREG = CONTROL_STARTCYCLE ;     /* start a measurement cycle */

#ifdef SHOW_CONFIG
    if (wait_for_buffer_to(&mybuff, 20))
    { /* say hello */
       disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
       sprintf(mybuff.buffer,"\r\n-------- Reflection Measurement, under development, date: %s, time: %s --------\r\n",
               CompileDate, CompileTime) ;
       atx_send(&mybuff) ;
    }

    if (wait_for_buffer_to(&mybuff, 20))
    {
       tmp_ui = *SI_CPUFREQ  / 1000 ;       /* Hz -> kHz */
       tmp_uj = *SI_SAMPLINGFREQ / 1000 ;   /* Hz -> kHz */
       disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
       sprintf(mybuff.buffer,"CPU clock: %3u.%03u MHz - Sampling clock: %3u.%03u MHz - Measurement interval: %u ms\r\n",
       tmp_ui/1000, tmp_ui%1000,
       tmp_uj/1000, tmp_uj%1000,
       it_ms) ;
       atx_send(&mybuff) ;
    }
#endif

#ifdef TEST_TEMPERATURE_DECODING
    test_tempdecode() ;
#endif

    while(1)
    {
        status = *RM_SREG ;
        if ((status & STATUS_VALID) != 0)
        { /* measurement result available */
            phase_100 = (*RM_PDC * 18) / (RM_CLKS / 1000) ; /* phase shift in 1/100 degrees */
            f_quality = (*RM_FHIGH * 10) / (RM_CLKS / 1000) ;
            r_quality = (*RM_RHIGH * 10) / (RM_CLKS / 1000) ;

            f_leads_r = *RM_EC1 ;
            r_leads_f = *RM_EC2 ;
            f_khz = *RM_FC1 / it_ms ;
            r_khz = *RM_FC2 / it_ms ;
            shift = (f_leads_r > r_leads_f) ? "-" : "+" ;


#ifdef SHOW_PHASE_MEASUREMENT
            if (wait_for_buffer_to(&mybuff, 20))
            {
//            *TRIGGER_LA = TRIGGER0 ;
                disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
                sprintf(mybuff.buffer,"Freq_f: %3u.%03u MHz, Freq_r: %3u.%03u MHz, Phase-Diff: %s%u.%02u°, High_f: %u.%02u%%, High_r: %u.%02u%%\r",
                        f_khz/1000, f_khz%1000,
                        r_khz/1000, r_khz%1000,
                        shift, phase_100 / 100, phase_100 % 100,
                        f_quality / 100, f_quality % 100,
                        r_quality / 100, r_quality % 100
                        ) ;
//            *TRIGGER_LA = TRIGGER1 ;
                atx_send(&mybuff) ;
            }
#endif

#ifdef SHOW_ALL_RESULTS
            if (wait_for_buffer_to(&mybuff, 20))
            {
                adc_temperature = adct() ;
                disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
                sprintf(mybuff.buffer,"Ff: %3u.%03u MHz, Fr: %3u.%03u MHz, Delta_P: %s%u.%02u°, High_f: %u.%02u%%, High_r: %u.%02u%%, ADC0=%u mV, ADC1=%u mV, ADC2=%u mV, ADC3=%u mV, T=%c%d,%03d °C  \r",
                        f_khz/1000, f_khz%1000,
                        r_khz/1000, r_khz%1000,
                        shift, phase_100 / 100, phase_100 % 100,
                        f_quality / 100, f_quality % 100,
                        r_quality / 100, r_quality % 100,
                        adc0(),
                        adc1(),
                        adc2(),
                        adc3(),
                        (adc_temperature>=0)?'+':'-',
                        abs(adc_temperature/1000),
                        abs(adc_temperature%1000) ) ;
                atx_send(&mybuff) ;
            }
#endif

#ifdef SHOW_ALL_SIMPLIFIED
            if (wait_for_buffer_to(&mybuff, 20))
            {
                adc_temperature = adct() ;
                disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
                sprintf(mybuff.buffer,"%06u %06u %05u %04u %04u %04u %04u %06u\r",
                        f_khz,
                        r_khz,
                        phase_100,
                        adc0(),
                        adc1(),
                        adc2(),
                        adc3(),
                        adc_temperature) ;
                atx_send(&mybuff) ;
            }
#endif
            *RM_CREG = CONTROL_STARTCYCLE ;  /* start next measurement cycle */
        }

        if (flag_10ms) task_10ms() ;   // execute regular tasks

#ifdef SHOW_ADC_RESULTS
        if (trigger_tx)
        { /* execute once per 100 milliseconds */
           if (wait_for_buffer_to(&mybuff, 20))
           {
               adc_temperature = adct() ;
               disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
               sprintf(mybuff.buffer,"ADC0=%u mV, ADC1=%u mV, ADC2=%u mV, ADC3=%u mV, T=%c%d,%03d °C                \r",
                      adc0(),
                      adc1(),
                      adc2(),
                      adc3(),
                      (adc_temperature>=0)?'+':'-',
                      abs(adc_temperature/1000),
                      abs(adc_temperature%1000) ) ;
               atx_send(&mybuff) ;
               trigger_tx = false ;
           }
        }
#endif
    }

  return 0;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       atx_send(TXBUFFER *)                                                                      │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Transmit a buffer via ATx                                                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void atx_send(TXBUFFER *tx)
{
    tx->next = 1 ;
    tx->completed = false ;
    atx_currentbuffer = tx ;
    *AT_TXB = tx->buffer[0] ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       wait_for_buffer_to(TXBUFFER *, uint32_t timeout)                                          │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ active wait for transmit buffer available with timeout                                          │
│                                                                                                 │
│ Returns: true if buffer is available, false otherwise                                           │
│                                                                                                 │
│ Timeout in ms, granularity is 10 ms.                                                            │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
bool wait_for_buffer_to(TXBUFFER *tx, uint32_t timeout)
{
bool retval ;
    retval = false ;
    wait_to = (timeout + 10) / 10 ;
    if (tx != NULL)
    {
        while ((!tx->completed) && (wait_to != 0)) ;
        if (tx->completed) retval = true ;
    }
    return(retval) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       user0_int()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User interrupt 0: ATx interrupt handler, transmit buffer is empty                               │
│                                                                                                 │
│ Executed in interrupt context!                                                                  │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void user0_int()
{
char tmp ;
    if (atx_currentbuffer != NULL)
    {
        tmp = atx_currentbuffer->buffer[atx_currentbuffer->next] ;
        if ((tmp != '\0') && (atx_currentbuffer->next < atx_currentbuffer->size))
        {
           *AT_TXB = tmp ;
           atx_currentbuffer->next++ ;
        }
        else atx_currentbuffer->completed = true ;
    }
    irq_enable_one_bit(INTNUM_USER0) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       user1_int()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ User interrupt 1: Interval timer interrupt handler                                              │
│                                                                                                 │
│ Executed in interrupt context!                                                                  │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void user1_int()
{
//    *TRIGGER_LA = TRIGGER2 ;
    flag_10ms = true ;
    if (wait_to != 0) wait_to-- ;

    if (tx_interval != 0) tx_interval-- ;
    else
    { /* time to transmit the results */
       tx_interval = TRANSMIT_INTERVAL - 1 ;
       trigger_tx = true ;
    }

    read_adc_raw() ;

    irq_enable_one_bit(INTNUM_USER1) ;
//    *TRIGGER_LA = TRIGGER3 ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       timer_int()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Timer interrupt handler *** unused ***                                                          │
│                                                                                                 │
│ Executed every 10 ms in interrupt context                                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void timer_int()
{
    set_timer(*SI_CPUFREQ/100);       /* next timer interrupt in 10ms */
    irq_enable_one_bit(INTNUM_TIMER) ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       task_10ms()                                                                               │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ execute every 10 ms, triggered by 10 ms interval timer interrupt, but executed in user context  │
│                                                                                                 │
│ Dont rely on the interval. If main loop takes too long, execution may be skipped.               │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void task_10ms()
{
    flag_10ms = false ;
//    read_adc() ;
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       test_tempdecode()                                                                         │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Test temperature decoding                                                                       │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
void test_tempdecode()
{
uint16_t dp ;
int32_t temperature ;
uint32_t result ;
int32_t divider ;
static char format[100] ;

    for (dp=0; dp<=5; dp++)
    { /* number of decimal places */

        switch (dp)
        { /* reception completed. Check for error, otherwise check address and prepare response */
           case 0: divider =      1 ; break ;
           case 1: divider =     10 ; break ;
           case 2: divider =    100 ; break ;
           case 3: divider =   1000 ; break ;
           case 4: divider =  10000 ; break ;
           case 5: divider = 100000 ; break ;
           default: divider =     1 ; break ;
        }

        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer,"Number of decimal places: %d\r\n", dp) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x1000, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +128,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0fff, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +127,96875 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0c80, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +100,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0960, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +75,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0640, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +50,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0320, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +25,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0008, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +0,25000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0001, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +0,03125 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x0000, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: +0,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x3ff8, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: -0,25000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x3ce0, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: -25,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }

        temperature = make_ads1118_temperature(0x3b00, dp) ;
        disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
        sprintf(format,"Temperature: %%c%%d,%%0%dd °C (should be: -40,00000 °C)\r\n", dp) ;
        if (wait_for_buffer_to(&mybuff, 20))
        {
            disable_interrupt_global() ;    /* Disable global interrupts, sprintf will reenable interrupts, see "important precaution" above */
            sprintf(mybuff.buffer, format, (temperature>=0)?'+':'-', abs(temperature/divider), abs(temperature%divider)) ;
            atx_send(&mybuff) ;
        }
    }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       read_adc_raw()                                                                            │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Read and save raw ADC result and start next conversion cycle                                    │
│                                                                                                 │
│ This function is called in the timer interrupt context, i.e. every 10ms.                        │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯

    Since this function is called in the timer interrupt context, its execution time is keept as
    short as possible. Therefore only the raw result from the previous conversion is read from the
    SPI receive register and the next conversion is triggered.

*/
void read_adc_raw()
{
uint32_t rx_data ;

    wbspi_master_select_slave(PICO_WBSPI_MASTER, 0) ; /* may not be required */

    if ((PICO_WBSPI_MASTER->STATUS & (WBSPI_ROE | WBSPI_TOE)) != 0)
    { /* error condition: receive buffer or transmit buffer overflow */
        PICO_WBSPI_MASTER->STATUS = (WBSPI_ROE | WBSPI_TOE) ; /* clear errors */
        adcraw_sm = ADC_IDLE ; /* force idle state */
    }

    if ((PICO_WBSPI_MASTER->STATUS & WBSPI_RRDY) != 0)
    { /* receive buffer is full */
        rx_data = PICO_WBSPI_MASTER->RXDATA >> 16 ; /* read rx data */
    }
    else
    { /* no data, force idle state */
        adcraw_sm = ADC_IDLE ;
    }

/* state machine for raw ADC handling */
    switch (adcraw_sm)
    { /* store result and start next conversion */
       case ADC_IDLE: /* idle, start next conversion */
           PICO_WBSPI_MASTER->TXDATA = ADC_CONFIG0 ;
           adcraw_sm = ADC_CONVERT0 ;
       break ;

       case ADC_CONVERT0: /* AIN0: VIN supply via 15k-1k */
           adc_rawresults[4] = rx_data ; /* temperature received */
           PICO_WBSPI_MASTER->TXDATA = ADC_CONFIG1 ;
           adcraw_sm = ADC_CONVERT1 ;
       break ;

       case ADC_CONVERT1: /* AIN1: 3,3V supply */
           adc_rawresults[0] = rx_data ; /* adc0 received */
           PICO_WBSPI_MASTER->TXDATA = ADC_CONFIG2 ;
           adcraw_sm = ADC_CONVERT2 ;
       break ;

       case ADC_CONVERT2: /* AIN2: LOGUF */
           adc_rawresults[1] = rx_data ; /* adc1 received */
           PICO_WBSPI_MASTER->TXDATA = ADC_CONFIG3 ;
           adcraw_sm = ADC_CONVERT3 ;
       break ;

       case ADC_CONVERT3: /* AIN3: LOGUR */
           adc_rawresults[2] = rx_data ; /* adc2 received */
           PICO_WBSPI_MASTER->TXDATA = ADC_CONFIGT ;
           adcraw_sm = ADC_CONVERTT ;
       break ;

       case ADC_CONVERTT:
           adc_rawresults[3] = rx_data ; /* adc3 received */
           PICO_WBSPI_MASTER->TXDATA = ADC_CONFIG0 ;
           adcraw_sm = ADC_CONVERT0 ;
       break ;

       default:
           adcraw_sm = ADC_IDLE ;
       break ;
    }
}

/*
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
│       make_ads1118_temperature(uint16_t raw, uint16_t dp)                                       │
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                                 │
│ Convert ADS1118 digital output to temperature                                                   │
│                                                                                                 │
│ raw: the 14-bit value from the temperature sensor                                               │
│ dp:  number of fractional decimal places                                                        │
│                                                                                                 │
│ returns: rounded temperature in °C * 10**dp                                                     │
│                                                                                                 │
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
*/
/*
from ADS1118 data sheet:
Converting from Digital Codes to Temperature
To convert from digital codes to temperature, first check whether the MSB is a 0 or a 1. If the MSB is a 0,
simply multiply the decimal code by 0.03125°C to obtain the result. If the MSB = 1, subtract 1 from the result
and complement all of the bits. Then multiply the result by –0.03125°C.
Example: The device reads back 0960h: 0960h has an MSB = 0.
0960h × 0.03125°C = 2400 × 0.03125°C = 75°C
Example: The device reads back 3CE0h: 3CE0h has an MSB = 1.
Subtract 1 and complement the result: 3CE0h → 0320h
0320h × (–0.03125°C) = 800 × (–0.03125°C) = –25°C
*/
int32_t make_ads1118_temperature(uint16_t raw, uint16_t dp)
{
int32_t temperature ;
int32_t multiplicator ;

    switch (dp)
    { /* reception completed. Check for error, otherwise check address and prepare response */
       case 0: multiplicator = 100000 ; break ;
       case 1: multiplicator =  10000 ; break ;
       case 2: multiplicator =   1000 ; break ;
       case 3: multiplicator =    100 ; break ;
       case 4: multiplicator =     10 ; break ;
       case 5: multiplicator =      1 ; break ;
       default: multiplicator =     1 ; break ;
    }

    if ((raw & 0x2000) == 0)
    { /* positive value */
       temperature = raw * 3125 ;
       temperature += multiplicator / 2 ;
       temperature /= multiplicator ;
    }
    else
    { /* negative value */
       temperature = ((int32_t) (~(raw-1) & 0x3fff)) * 3125 ;
       temperature += multiplicator / 2 ;
       temperature /= multiplicator ;
       temperature = -temperature ;
    }
    return(temperature) ;
}