target icon

Purpose

This tutorial shows a step by step process to use the online toolkit, create a project, edit a project, compile, build, download the binary file, flash the binary file in the Pyboard and start execution.

list icon

Prerequisites

  • A complete installation of an Hyperpanel OS release, already installed on a Linux PC, including tools for flashing and debugging. You need at least two USB ports on the computer (one for flashing and running, one for terminal).
  • Internet access for the online Toolkit.
  • A registered account on www.hyperpanel.com
  • A Pyboard kit provided by HyperPanel Lab.

list icon

Software release

hypv9wr58 and higher

align left icon

Description

1.

Check that you have everything necessary regarding the Pyboard:

 

  • The Pyboard and the training daughter board from HyperPanel Lab (pict01).
  • The Olimex ARM-USB-OCD interface for software flashing (pict02).
  • The cable for Olimex connection to the PyBoard (pict03).
  • The USB/ASY cable used for terminal (pict04).
  • The USB cable to connect the Pyboard to the computer (pict05).

2.

Plug the daughter board on the Pyboard. Be very carefull, the pins are delicate (pict06).

3.

Connect the Olimex cable connector from the Olimex, to the Pyboard (pict07).

4.

Connect the Olimex to an USB port of the computer (pict08). The green light on the Olimex turns ON. Check that a device (like ttyUSB0) appears in your /dev system directory (pict09).

5.

Connect the ASY0 socket of the daughter board on another USB port of the computer (pict10). Check carefully the colors of cables corresponding to the socket pins as follow:

  • Red V (power)
  • Black GND (ground)
  • White TX (data)
  • Green RX (data)

Check that a device (like ttyUSB1) appears in your /dev system directory (pict11).

6.

On your web browser, go to : www.hyperpanel.com and select “Toolkit” (yellow button in main menu). Entre your Username (or e-mail) and your password (pict12) (you need a valid account provided by HyperPanel Lab).

7.

Create a new project (“New project” button), give a name (for example “Tutorial-113”), select the platform and the Version (pic13):

  •  Name : Tutorial-113
  • Platform : stm32py
  • Version : hypv9wr61

Press “Confirm”, and empty project is created.

8.

Update software Library (“Library” button), and select “Your first app” files package by press the “Include” button, press “Confirm” (pict14).

9.

You have now access to the default application (pict15), that you can edit, compile, build, and download for flashing in the PyBoard.

10.

As a first exemple, let’s try to flash the Pyboard with this application. Don’t change anything. First step is the compilation. Press the “Compile” button, wait for compilation ending, and check that in the console, there is no error and the green message “Compilation success!” (pict16).

11.

Building and downloading: Press the “Build” button, wait for the builder ending. If everything is ok, the green message “Build success!” is displayed and a pop-up appears for download confirmation (pict17), select “OK” and download the zip file (“Tutorial-113.zip”).

12.

On the computer, open a shell and go into the directory of your HyperPanel environment software relase (9wr61 in this tutorial), and set environment with following command:

cd ~/hypv9wr61/os/linux
source stm32py
exe

copy the zip file in exe directory and unzip the file:

cp ~/Downloads/Tutorial-113.zip ~/hypv9wr61/stm32py/exe
unzip Tutorial-113.zip

You have now two files (pict18):

 

  • hypos.bin Binary file for flashing.
  • hypos.elf “elf” file use by flashing tools.

13.

Flashing the software with following commands:

exe
hgdb
romload stm32py hypos

Wait for flashing process termination. The last message in the console is something like (pict19):

auto erase enabled
wrote 393216 bytes from file hypos.bin in 12.650161s (30.355 KiB/s)
(gdb)

14.

Start execution: open another shell and launch an asy tool console (for exemple “minicom” because configuration file is available in the release).

  • Enter following commands:
cd ~/hypv9wr61/os/linux
source stm32py
exe
minicom python

 

  • The minicom starts (pict20).
  • To start the software, press on the Reset button (“RST1”) of the daughter board (pict21).
  • The app starts. The menu appears on the minicom (pict22), and if you press on the buttons (“BUT1”, “BUT2”, “BUT3”) of the daughter board, the red, green yellow LEDs start to blink (mov01).

bulb icon

Notes

  • If you want to start the sofware already flashed in the Pyboard without flashing, just use “rom” command instead of “romload”:
exe
hgdb romload stm32py hypos

 

  • A easy way to modify the app and see the result is to edit the file “mayapp_scr.txt” in the Toolkit (for example add some wording modifications in the menus) and do compilation / build / download / flashing actions.

App execution

Code

hypos-tuto-113-full.c

/*
**  Myapp.c - Sample code for STM32 HyperPanel OS ============== 02/08/19 =  **
**                                                                           **
**  The Nucleo STM32L152RE board has to be extended with an Arduino shield   **
**  with 3 LED's (REG, GREEN and YELLOW), 3 buttons and infrared receiver.   **
**  We are using USART 2 that is routed throught the USB debug link as a     **
**  console. One can use minicom as an asynchronous terminal emulator.       **
**                                                                           **
**  This simple code is located into the Application Container, it is run    **
**  by the VMK sub-operation system. On the other hand, the I/O container    **
**  runs the ASY (Asynchronous Lines) driver, the GPIO driver (General       **
**  Purpose IO lines), the LED driver, the KBDITF driver (keyboard interface,**
**  including remote control unit RCU) and a DAC (Digital to Analog          **
**  converter) driver. Those five drivers are run by the VMIO sub-OS.        **
**                                                                           **
**  This sample code illustrates the unrivaled OS architecture whereas user  **
**  tasks are just hidden FSM's. This sample code is made of one very simple **
**  user FSM and one user task. The FSM may be requested to continuously     **
**  sends one same data buffer on serial line ASY1, to the DAC, or both. The **
**  user task manages a user interface (UI) through the ASY0 serial line.    **
**  This UI is event driven and receives events from the ASY0 serial line    **
**  (user command from the minicom keyboard), from the 3 buttons of the      **
**  Arduino shield and from an Infrared RCU. The UI also receives each       **
**  second a time-out event in order to refresh the displayed time. The user **
**  task accepts at any time any incoming data on the ASY1 serial line and   **
**  if the FSM is not currently transmitting data the UI task "echoes" on    **
**  ASY1 the incoming data. If the FSM has been requested to transmit on     **
**  ASY1 then incoming data is not echoed. The user FSM is using non         **
**  blocking writes only while the user task only uses blocking writes for   **
**  ASY0 and non blocking writes for ASY1. The user task is doing non        **
**  blocking reads on both ASY0 and ASY1.                                    **
**                                                                           **
**  The sample code also illustrates that one FSM and one task can very      **
**  easily exchange events, that may be used as requests, responses or       **
**  synchronization. Here the task sends SND_START and SND_STOP request      **
**  events and the FSM responds with R_SND_START and R_SND_STOP events.      **
**                                                                           **
**  The two container's OS and this sample application do not need more RAM  **
**  than the STM32L152RE 80 KB of RAM memory.                                **
**                                                                           **
**  ======================================================================== **
**                                                                           **
**  This application is using the driver/protocol/service API to send        **
**  requests to the drivers. Those requests are in fact event messages that  **
**  are received by the automatons located inside the I/O container. The     **
**  ASY driver is one automaton AUT_ASY that has one logical way for each    **
**  USART. The GPIO driver has one logical way for the GPIO controller plus  **
**  one logical way for each PIN, here we are using three pins, one for      **
**  each button. The LED driver has one logical way for the LED controller   **
**  plus one logical way for each subchannel (each LED is a subchannel). The **
**  The KBDITF driver has one single logical way for all "keyboard" events,  **
**  including RCU. The DAC driver has one logical way per DAC device.        **
**                                                                           **
**  The ASY driver includes all the functions of an advanced asynchronous    **
**  line driver. It handlers multiple controller's, those may be             **
**  heterogenous, supports all kind of line parameters, supports all kinds   **
**  of input and output flow control, is able to buffer data while the       **
**  applicative software is busy, is able to handler DMA transfers and is    **
**  able to handle very precisely line idle times.                           **
**                                                                           **
**  The LED driver includes an automatic blinking function. The VMK code     **
**  has only to provide a "blinking" pattern and the LED driver repeats the  **
**  pattern without disturbing the VMK code. So this simple code does not    **
**  contain anything related to blink pattern management.                    **
**                                                                           **
**  The GPIO driver includes a "de-bounce" function for input pins that are  **
**  connected to "dirty" buttons. Also, this simple code does not have to    **
**  manage "de-bouncing" of buttons. The GPIO driver is able to              **
**  simultaneously poll a great number of GPIO's and use interrupts for a    **
**  limited set (hardware limit) for wich zero latency is needed. The use    **
**  of polling or IRQ's is dynamically done by the applicative software      **
**  When IRQ is selected, then hardware signal filtering may be enabled.     **
**                                                                           **
**  The KBD driver is an interface to both GPIO's used for push-buttons and  **
**  also to a RCU receiver driver that is able to handle all the known       **
**  infra-red RCU protocols (NEC 2000, RC5, RC6, XMP, ...).                  **
**                                                                           **
**  The DAC driver is able to manage several DAC's concurrently, it supports **
**  a wide range of sampling frequencies, is able to LUT (Look Up Tables)    **
**  for values transcoding. This may be use for example to adjust signal     **
**  level.                                                                   **
**                                                                           **
**  ======================================================================== **
**                                                                           **
**  The "space_appcont_xyz.c" file contains the definition of a table named  **
**  "task_grp" that has two entries of type "Task_grp". The first entry      **
**  contains the name "fsm" and the address of the procedure "init_app_fsm". **
**  The second entry contains the name "tsk" and the address of procedure    **
**  "init_app_tsk". At the very end of its initialization, the VMK sub-OS    **
**  starts the tasks groups, it calls the group start procedure's, so in     **
**  our case the "init_app_fsm" procedure is called and then the             **
**  "init_app_tsk" is called. Those call are done using the VMK stack, its   **
**  address and size are definied uin the linker's command file :            **
**  RAM_VMK_STACK        : ORIGIN = 0x2000C800 , LENGTH = 2K                 **
**                                                                           **
**  The first task group "fsm" contains one and only one "system" task (a    **
**  hardware context) which is associated to a "task_id" is 0. Do not be     **
**  confused, it is NOT a user task. In fact it corresponds to a hardware    **
**  context that is mainly a dedicated system stack. This context is         **
**  systematically created at VMK initialisation. The stack address and size **
**  are defined in the command link file:                                    **
**  RAM_VMOS_STACK       : ORIGIN = 0x2000D000 , LENGTH = 2K                 **
**  The C treatments of all the APP FSM's are executed in context 0, so      **
**  they use the task 0 stack. With this simple demo program, the            **
**  "init_vmk_fsm" procedure only delares one FSM using the "iniaut"         **
**  function and then sends one initialization event to this FSM.            **
**                                                                           **
**  The second task group "tsk" contains all the user tasks, the task id's   **
**  are greater or equal to 1. Each has its own stack, the address and size  **
**  of it are given to the "create_task" procedure. With this demo code,     **
**  the "init_app_tsk" procedure only creates one user task and its stack is **
**  simply a C global variable "mystack[]". Also, the task entry point has   **
**  to be given to "create_task", in our case it is the "loop_app_tsk" C     **
**  procedure                                                                **
**                                                                           **
**  ======================================================================== **
**                                                                           **
**  The shield has 3 leds RED, GREEN and YELLOW. It has 3 switch buttons,    **
**  one 56 KHz Infrared Receiver. It has also 3 connectors 6 pins, 10 pins   **
**  and 10 pins. The electrical signals that are used by "myapp.c" are       **
**  depicted below                                                           **
**                                                                           **
**                           ASY1_RX  ASY1_TX    GND                         **
**                              X        X        X                          **
**                              X        X        X                          **
**                           ASY1_RX  ASY1_TX    GND                         **
**                                                                           **
**                                               GND                         **
**            X        X        X        X        X                          **
**            X        X        X        X        X                          **
**                                               GND                         **
**                                                                           **
**                                    DAC_OUT    GND                         **
**            X        X        X        X        X                          **
**            X        X        X        X        X                          **
**                                    DAC_OUT    GND                         **
**                                                                           **
**  All the drivers have a "module" name that is given to "drv_init_driver"  **
**  that returns and identifier we call a "MODD". All the devices have       **
**  a name that is given to "drv_alloc_device" that returns and identifier   **
**  we call a "device IOD". With devices that have "sub-channels" each       **
**  sub-channel have one or more name that is given to "drv_alloc_subchan"   **
**  that returns and identifier we call a "sub-channel IOD". The present     **
**  application is using the following names and identifiers :               **
**                                                                           **
**  +-----------------+---------------------+-----------+-----------------+  **
**  | Name       MODD | Name        dev IOD | schan IOD | Physical        |  **
**  +-----------------+---------------------+-----------+-----------------+  **
**  | ASY      myio.c | ASY\DEV0   asy_iod0 |           | USB cable (*)   |  **
**  |                 | ASY\DEV1   asy_iod1 |           | ASY1_RX ASY1_TX |  **
**  |                 | ASY\DEV2   asy_iod2 |           | ASY1_RX ASY1_TX |  **
**  +-----------------+---------------------+-----------+-----------------+  **
**  | GPIO     myio.c | GPIO\DEV0  gpio_iod | gpio_iod1 | Switch 1        |  **
**  |                 |                     | gpio_iod2 | Switch 2        |  **
**  |                 |                     | gpio_iod3 | Switch 3        |  **
**  +-----------------+---------------------+-----------+-----------------+  **
**  | LED      myio.c | LED\DEV0    led_iod | led_iod0  | RED Led         |  **
**  |                 |                     | led_iod1  | GREEN Led       |  **
**  |                 |                     | led_iod2  | YELLOW Led      |  **
**  +-----------------+---------------------+-----------+-----------------+  **
**  | KBDITF   myio.c | KDBIDF\DEV0 kbd_iod |           | Infrared Rcv    |  **
**  +-----------------+---------------------+-----------+-----------------+  **
**  | DAC      myio.c | DAC\DEV0    dac_iod |           | DAC_OUT         |  **
**  +-----------------+---------------------+-----------+-----------------+  **
**                                                                           **
**  (*) The RX and TX signal of ASY0 are connected to an external chip       **
**      that is a serial <--> Usb converter. Therefore data that is send     **
**      using ASY0 goes throught the USB cable                               **
**                                                                           **
**  The returned identifers are all global variables. With devices that do   **
**  not have sub-channels (ASY, KDBITF and DAC) the software must use the    **
**  device IOD in order to use the device (asy_iod0, asy_iod1, asy_iod2,     **
**  kbd_iod and dac_iod). With devices that have sub-channels, then the sub- **
**  channel IOD is used in order to interact with the sub-channel            **
**  (gpio_iod1, gpio_iod2, gpio_iod3 for switching buttons and led_iod0,     **
**  led_iod1, led_iod2 for Leds).                                            **
**                                                                           **
**  =======================================================================  **
**  commenter a partir de open_asy
*/


/* Includes files and external references ...................................*/

#include <hypos.h>                     // HyperpanelOS interfaces

#include <drv_rcu.h>                   // RCU procedures prototypes
#include <drv_i2c.h>                   // i2c_send, i2c_receive

#include "./myio.h"                    // Prototypes for "myio_xxx" procs
#include "./mytsk.h"                   // Prototypes for "loop_app_tsk"
#include "./myapp.h"                   // Constant and structs for myapp.c

#include <keym.txt>                    // Applicative key codes


/* Internal global variables for the AUT_USND automaton ---------------------*/

unsigned short      state_usnd[VLNB] ; // State variables for AUT_USND
unsigned char       sp_usnd [VLNB]   ; // Stack pointers for AUT_USND
S_evt               evt_usnd         ; // Current event for AUT_USND
Usnd_ctx            usnd_ctx[VLNB]   ; // Context tables for AUT_USND


/* Internal global variables for the user task ------------------------------*/

int                 idto             ; // Timer identifier
int                 flag_rest        ; // Flag for restart
int                 errcode          ; // Treatment error code

int                 asy_iod0         ; // IOD of devive ASY\DEV0
int                 asy_iod1         ; // IOD of device ASY\DEV1
int                 asy_iod2         ; // IOD of device ASY\DEV2

int                 gpio_iod         ; // IOD of device GPIO\DEV0
int                 gpio_iod1        ; // IOD of subchannel 0 of GPIO\DEV0
int                 gpio_iod2        ; // IOD of subchannel 1 of GPIO\DEV0
int                 gpio_iod3        ; // IOD of subchannel 2 of GPIO\DEV0

int                 led_iod          ; // IOD of device LED\DEV0
int                 led_iod0         ; // IOD of subchannel 0 of LED\DEV0
int                 led_iod1         ; // IOD of subchannel 1 of LED\DEV0
int                 led_iod2         ; // IOD of subchannel 2 of LED\DEV0

int                 kbd_iod          ; // IOD of device KBDITF\DEV0
int                 rcusnd_id        ; // ID for RCU SND
int                 rcurcv_id        ; // ID for RCU RCV

int                 dac_iod          ; // IOD of device DAC\DEV0

unsigned char       *buf_aud         ; // Address of DEV0 audio buffer

unsigned int        page             ; // Menu current page, UI state variable
char                bufchr[LGCHR+1]  ; // Menu line input (integer number)
int                 lgchr            ; // Count of characters in bufchr

int                 ev_val           ; // Returned by "wait_myevent"
int                 opt_val          ; // Returned by "ev_opt"
int                 condcode         ; // Condition for FSM engine
int                 condret          ; // Condition of return

unsigned int        cfg[VMAX]        ; // Current serial+gpio configuration
int                 iods[VMAX]       ; // IOD to be used with each parameter

unsigned char      *buf_snd[ASYMAX]  ; // Address of transmit buffer
unsigned char      *buf_rcv[ASYMAX]  ; // Address of received buffer
int                 tok_rcv[ASYMAX]  ; // Count of receive tokens
int                 cnt_snd[ASYMAX]  ; // Count of ongoing sent messages

unsigned char      *buf_rcu          ; // Address of RCU send buffer

unsigned char       i2c_tx_frame[256]; // Buffer containing the IC2 frame to 
                                       // transmit
unsigned char       i2c_tx[256]      ; // Buffer containing bytes to tranmit
unsigned char       i2c_rx[256]      ; // Buffer containing the received bytes
I2c_io              i2c_iolist[2]    ; // Buffer containing struct describing 
                                       // frames to be transmitted
Lcd_text            i2c_text[2] =      // Text to display on the screen LCD
                      {                //
                        {              // Color coding: bbbb bggg gggr rrrr
                          0x001F     , // Foreground color: red
                          0xFFFF     , // Background color: white
                          1          , // Horizontal coordinate
                          1          , // Vertical coordinate
                          "Hello"      // Text
                        }            , //
                        {              // Color coding: bbbb bggg gggr rrrr
                          0xF800     , // Foreground color: blue
                          0x07FF     , // Background color: yellow
                          20         , // Horizontal coordinate
                          15         , // Vertical coordinate
                          "world"      // Text
                        }              //
                      }              ; //

/* Static data for the AUT_USND FSM -----------------------------------------*/

                                       // Events codes ----------------------
#define SND_START    101               // Start request
#define SND_STOP     102               // Stop  request
#define R_SND_START  103               // Response to Start request
#define R_SND_STOP   104               // Response to Stop  request

                                       // State numbers ---------------------
#define STOPPED        0               // Sending is not started
                                       // VLASY1/VLASY2/VLDAC states --------
#define STARTED        1               // Sending is running
#define STOPPING       2               // SND_STOP has been received
                                       // VLSNDRCU --------------------------
#define SNDWAIT        3               // Waiting for end of sending
#define TOWAIT         4               // Sleeping, waiting for a TO_RCU
#define LASTWAIT       5               // Waiting for the last end of sending

#define STATENB        6               // Transition table size



static S_trans const trans_usnd[] =
  {

/*****************************************************************************
 * Transition table for the AUT_USND VMK FSM                                 *
 * -----------------------------------------                                 *
 *                                                                           *
 * This VMK FSM receives one SND_START event that holds one data buffer      *
 * address and one IOD device. This buffer is send again and again. When the *
 * SND_STOP event is received then no more transmission is asked to the      *
 * device driver. Each locical way has one dedicated context that is a       *
 * "Usnd_ctx" structure. We have a context table, the "usnd_ctx[VLNB]" array.*
 *                                                                           *
 * The automaton treatements are the same ones for ASY1/ASY2 and DAC because *
 * those 3 devices are using the same device API from "myio.c". The RCU      *
 * receiver device uses the dedicated RCU system API and we chose to clearly *
 * separate the FSM treatements rather that making tests or switch in each   *
 * FSM treatement. Therefore the present transition table can be divided in  *
 * 3 blocks:                                                                 *
 *                                                                           *
 * - The first STOPPED state is common to VLASY1/VLASY2/VLDAC/VLSNDRCU       *
 * - STARTED and STOPPING are common to VLASY1/VLASY2/VLDAC                  *
 * - SNDWAIT, TOWAIT and LASTWAIT are dedicated to VLSNDRCU                  *
 *                                                                           *
 * - STOPPED   No transmission is done. We are waiting for a SND_START       *
 *             event request in order to start sending.                      *
 *                           ----------------------------------------------- *
 *             . SND_START   This event is a request to start sending. A     *
 *                           dedicated subroutine "req_snd_start" is used    *
 *                           to send it. This event contains the automaton/  *
 *                           logical way of the sender, its event waiting    *
 *                           queue number, the device IOD to be used for     *
 *                           sending, the address of the data buffer that    *
 *                           will be repeatedly sent and the data bytes      *
 *                           count. The "usnd_start" treatement is called,   *
 *                           it stores those 6 values in the context and     *
 *                           then it issues two calls to the "myio_send".    *
 *                           Those calls are non blocking since the device   *
 *                           has been allocated (drv_alloc_device) using the *
 *                           NBLOCKING_IO option bit. So the device generates*
 *                           immediately (0 latency) two REQ_WRITE event     *
 *                           requests and transmission corresponding to the  *
 *                           first REQ_WRITE immediately starts. The last    *
 *                           action of 'usnd_start" is to return a           *
 *                           R_SND_START response event to the one that      *
 *                           sent us the SND_START. This response event will *
 *                           re-schedule the "req_snd_start" procedure that  *
 *                           was waiting for it. Then we go to the STARTED   *
 *                           state.                                          *
 *                           ----------------------------------------------- *
 *             . C_RCU       This condition is set if the SND_START event    *
 *                           request is for VLSNDRCU. The consequence of     *
 *                           this condition is to go to state SNDWAIT        *
 *                           ----------------------------------------------- *
 *             . SND_STOP    This event is a request to stop sending. It     *
 *                           should never be received in the present state   *
 *                           since we did not yet received the SND_START.    *
 *****************************************************************************/

/* STOPPED (0) State : Waiting for the SND_START  event .....................*/
#define L_STOPPED  3

SND_START   , usnd_start  , 0     , STARTED , // Request to start transmitting
C_RCU       , urcu_send   , 0     , SNDWAIT , // .. We have a RCU
SND_STOP    , trien       , 0     , STOPPED , // Incorrect request here


/*****************************************************************************
 * States for VLASY1/VLASY2/VLDAC                                            *
 * ------------------------------                                            *
 *                                                                           *
 * - STARTED   The same data buffer is sent again and again. Each time the   *
 *             VMIO driver send us a RESP_WRITE, we issue a new "myio_send", *
 *             that sends one REQ_WRITE to the VMIO driver.                  *
 *                           ----------------------------------------------- *
 *             . SND_START   This event should never be received in the      *
 *                           STARTED state since we already have been        *
 *                           requested to start transmitting.                *
 *                           ----------------------------------------------- *
 *             . SND_STOP    This event is a request to stop sending. A      *
 *                           dedicated subroutine "req_snd_stop" is used to  *
 *                           send _t. This event contains the automaton and  *
 *                           logical way number of the sender and also the   *
 *                           internal queue number that we will have to use  *
 *                           for writing the response event. The "usnd_stop" *
 *                           treatment is called, it stores those 3 values   *
 *                           in the "Usnd_ctx" context structure. Then we go *
 *                           to the STOPPING state, we have two RESP_WRITE   *
 *                           events to be waited for.                        *
 *                           ----------------------------------------------- *
 *             . RESP_WRITE  This event is response event to one REQ_WRITE.  *
 *                           It is sent by the VMIO driver just after the    *
 *                           end of transmission of the last byte of the     *
 *                           message or at beginning of transmission of the  *
 *                           last byte of the message, depending on the      *
 *                           hardware capabilities. Just before sending us   *
 *                           the driver had started sending the next         *
 *                           message. So here we have "plenty" of time to    *
 *                           issue a new "myio_send" (the just started       *
 *                           message transmission duration). The "usnd_resp" *
 *                           treatment is called, it issues a call to the    *
 *                           "myio_send" function. A REQ_WRITE is written    *
 *                           in the REQ_WRITE driver's internal queue for    *
 *                           write requests with a 0 latency. So the driver  *
 *                           is now transmitting the very beginning of the   *
 *                           previous REQ_WRITE and has another REQ_WRITE in *
 *                           its write request queue. The automaton next     *
 *                           state is STARTED (no state change).             *
 *                                                                           *
 * - STOPPING  We received a SND_STOP event and we are waiting for the       *
 *             two remaining RESP_WRITE from the driver. Upon the second     *
 *             RESP_WRITE receive, we will go to the STOPPED state. When     *
 *             we enter the STOPPING state then the "ctx->nbreq" counter of  *
 *             awaited RESP_WRITE events is always equal to 2.               *
 *                           ----------------------------------------------- *
 *             . SND_START   This event is a request to start sending. It    *
 *                           should never be received in the present state   *
 *                           but only from the STOPPED state.                *
 *                           ----------------------------------------------- *
 *             . SND_STOP    This event is a request to stop sending. It     *
 *                           should never be received in the present state   *
 *                           but only from the STARTED state.                *
 *                           ----------------------------------------------- *
 *             . RESP_WRITE  Completion of one "req_write" that has been     *
 *                           issued from the STARTED state. This event is    *
 *                           sent by the VMIO driver. The "usnd_resp"        *
 *                           treatment is called, we decrement by 1 the      *
 *                           "ctx->nbreq" counter. If the counter value is   *
 *                           now 0 then we set the C_END condition           *
 *                           ----------------------------------------------- *
 *             . C_END       This not an event but a condition, that is set  *
 *                           when all the awaited RESP_WTITE events have     *
 *                           been received. The "usnd_end" treatment is      *
 *                           called: It send a R_SND_STOP response event,    *
 *                           using the "aur/vl/nfa" values that have been    *
 *                           stored in the context when the SND_STOP event   *
 *                           was received. The automaton next state is the   *
 *                           STOPPED state. The R_SND_STOP is received by    *
 *                           the "req_snd_stop" subroutine that was waiting  *
 *                           for it.                                         *
 *****************************************************************************/

/* STARTED (1) State : Waiting for the SND_STOP  event ......................*/
#define L_STARTED (L_STOPPED + 3)

SND_START   , trien       , 0     , STARTED , // Incorrect request here
SND_STOP    , usnd_stop   , 0     , STOPPING, // Request to stop transmitting
RESP_WRITE  , usnd_resp   , 0     , STARTED , // End of one transmission


/* STOPPING (2) State : Waiting for the 2 RESP_WRITE remaining events .......*/
#define L_STOPPING (L_STARTED + 4)

SND_START   , trien       , 0     , STOPPING, // Incorrect request here
SND_STOP    , trien       , 0     , STOPPING, // Incorrect request here
RESP_WRITE  , usnd_resp   , 1     , STOPPING, // End of one transmission
C_END       , usnd_end    , 0     , STOPPED , // . No more awaited RESP_WRITE


/*****************************************************************************
 * States for VLSNDRCU                                                       *
 * -------------------                                                       *
 * - SNDWAIT   A RCU message is currently sent and we are waiting for the    *
 *             RESP_WRITE end of transmission event.                         *
 *                           ----------------------------------------------- *
 *             . SND_START   This event should never be received in the      *
 *                           SNDWAIT state since we already have been        *
 *                           requested to start transmitting.                *
 *                           ----------------------------------------------- *
 *             . SND_STOP    This event is a request to stop sending. A      *
 *                           dedicated subroutine "req_snd_stop" is used to  *
 *                           send it. This event contains the automaton and  *
 *                           logical way number of the sender and also the   *
 *                           internal queue number that we will have to use  *
 *                           for writing the response event. The "usnd_stop" *
 *                           treatment is called, it stores those 3 values   *
 *                           in the "Usnd_ctx" context structure. We go to   *
 *                           the LASTWAIT state in order to wait for the     *
 *                           last RESP_WRITE event.                          *
 *                           ----------------------------------------------- *
 *             . RESP_WRITE  This event is generated by the RCU system       *
 *                           driver and its indicates that the last message  *
 *                           sending is completed. We start a TO_RCU time-   *
 *                           out because we want to wait for 5 seconds       *
 *                           before the next sending. The treatment that     *
 *                           starts the TO_RCU time-out is "urcu_tostart".   *
 *                           Then we go to TOWAIT in order to wait for the   *
 *                           TO_RCU event.                                   *
 *                                                                           *
 * - TOWAIT    A time-out is currently running and we are waiting for the    *
 *             TO_RCU event that will be sent by the VMOS to indicate that   *
 *             the waiting period is now over.
 *                           ----------------------------------------------- *
 *             . SND_START   This event should never be received in the      *
 *                           SNDWAIT state since we already have been        *
 *                           requested to start transmitting.                *
 *                           ----------------------------------------------- *
 *             . SND_STOP    This event is sent to us by the applicative     *
 *                           task, using the "req_snd_stop" subroutine. We   *
 *                           use the "urcu_stop" treatement that clears the  *
 *                           running TO_RCU and immediately sends the        *
 *                           R_SND_STOP response event to the task.          *
 *                           ----------------------------------------------- *
 *             . TO_RCU      This event is generated by the VMK, it notifies *
 *                           the end of the time-out. We execute the         *
 *                           "urcu_send" treatment that request to the RCU   *
 *                           driver to start transmitting a new message.     *
 *                                                                           *
 * - LASTWAIT  We received a SND_STOP event and we are waiting for the       *
 *             the RESP_WRITE that corresponds to the end of transmission    *
 *             of a message transmission that was ongoing when the SND_STOP  *
 *             event was received.                                           *
 *                           ----------------------------------------------- *
 *             . SND_START   This event should never be received in the      *
 *                           LAST state since we did not yet notified that   *
 *                           we would be halted.                             *
 *                           ----------------------------------------------- *
 *             . SND_STOP    We should not receive this event since we       *
 *                           already have been requested to stop and we did  *
 *                           not answered to it for now.                     *
 *                           ----------------------------------------------- *
 *             . RESP_WRITE  This is the event we are waiting for, sent by   *
 *                           the RCU system driver, that tells us that the   *
 *                           last RCU message transmission has been          *
 *                           completed. We execute the "usnd_end" treatment  *
 *                           that sends the R_SND_STOP response event, using *
 *                           the references that the "usnd_stop" treatement  *
 *                           had stored in the context. The next state is    *
 *                           the STOPPED state.                              *
 *****************************************************************************/

/* SNDWAIT (3) State : Waiting for the RESP_WRITE  ..........................*/
#define L_SNDWAIT (L_STOPPING + 3)

SND_START   , trien       , 0     , SNDWAIT , // Incorrect request here
SND_STOP    , usnd_stop   , 0     , LASTWAIT, // Request to stop transmitting
RESP_WRITE  , urcu_tostart, 0     , TOWAIT  , // End of one transmission

/* TOWAIT (4) State : Waiting for TO_RCU time-out event .....................*/
#define L_TOWAIT (L_SNDWAIT + 3)

SND_START   , trien       , 0     , TOWAIT  , // Incorrect request here
SND_STOP    , urcu_stop   , 0     , STOPPED , // Request to stop transmitting
TO_RCU      , urcu_send   , 0     , SNDWAIT , // Time-out notification

/* LASTWAIT (5) State : Waiting for the last RESP_WRITE  ....................*/
#define L_LASTWAIT (L_TOWAIT + 3)

SND_START   , trien       , 0     , LASTWAIT, // Incorrect request here
SND_STOP    , trien       , 0     , LASTWAIT, // Request to stop transmitting
RESP_WRITE  , usnd_end    , 0     , STOPPED , // End of one transmission

  }                                  ; // -----------------------------------

static USHORT const lim_usnd[] =       // List of state limit's in trans_usnd
  {                                    // -----------------------------------
    0         , L_STOPPED    ,         // 1st line for STOPPED, STARTED
    L_STARTED , L_STOPPING   ,         // 1st line for STOPPED, end of table
    L_SNDWAIT , L_TOWAIT     ,
    L_LASTWAIT,
  }                                  ; // -----------------------------------



/* Lists of tags for the "open_xyz" procedures ..............................*/

static const char *asy_taglist0 = "BAUDS=115200\nNBBITS=8\n"
                                  "STOPBIT=1\nPARITY=NONE"                   ;

static const char *asy_taglist1 = "BAUDS=9600\nNBBITS=8\n"
                                  "STOPBIT=1\nPARITY=NONE\nBUFSIZE=0\n"
                                  "FLOWCTL=NONE\nTMODE=NONE\nTEMPO=1"        ;

static const char *gpio_taglist = "FUNC=GPIO\nOUTPUT=HI_Z\nWEAKPULL=UP\n"
                                  "EVENTS=REPORT"                            ;

static const char *dac_taglist  = "DEPTH=8\nSFREQ=8000\nNBCH=2\nENDIAN=MSB"  ; 


#include "./myapp_scr.txt"

/* Beginning of the code ----------------------------------------------------

------------------  User FSM ------------------------------------------------
init_app_fsm        VMK user Finite State Machine's creation function

usnd_start          SND_START event treatment
usnd_stop           SND_STOP  event treatment for ASY1/ASY2/DAC
usnd_resp           RESP_WRITE event treatment
usnd_end            C_END condition treatment
urcu_send           C_RCU and TO_RCU treatment, send one RCU message
urcu_tostart        RESP_WRITE treatment, starts the TO_RCU time-out
urcu_stop           SND_STOP event treatment for RCU
req_snd_start       Send a SND_START event to the AUT_USND automaton
req_snd_stop        Send a SND_STOP  event to the AUT_USND automaton

------------------  User task main loop and termination ---------------------
loop_app_tsk        Entry point for create_task - Main event loop

------------------  User task devices initialization and termination --------
open_asy            Open of two serial ports
close_asy           Close of two serial ports
open_gpio           Open the GPIO driver
close_gpio          Close the GPIO driver
open_led            Open the LED driver
close_led           Close the LED driver
open_kbd            Open the KBDITF driver
close_kbd           Close the KBDITF driver
open_dac            Open the DAC driver
close_dac           Close the DAC driver

------------------  UI event driven treatments ------------------------------
empt                Do nothing
pmen                Print the menu on ASY\DEV0 and the time
ptim                Print the time in seconds
pgrp                Print the status strings
pcmd                Print the last selected option/character
pchr                Print echo of one ASCII character
pcap                Prints the RCU symbols captured values
perr                Print the "errcode" value
tcfg                Togles a "cfg[]" value
tinp                Togles VINPUT and VNUMRCU
tssy                Togles VONMIN/VONMAX/VSTMIN/VSTMAX
tchr                Add character to "bufchr
scfg                Sets a "cfg[]" value
sval                Do a drv_detval with a "cfg[]" value
sled                LED blinking start/stop
sint                Converts the ASCII decimal string from "buhchr" to integer
rasy                ASY1/ASY2 continuous sending start/stop
rdac                DAC  continuous sending start/stop
rrcu                RCU transmitter sending start/stop
ewreq               Write I2C EEPROM (offset specified)
eroreq              Read  I2C EEPROM (offset specified)
erreq               Read  I2C EEPROM (next bytes)
ldreq               Display text on screen LCD
lcreq               Clear screen LCD
ltcreq              Get touch coordinates
sapt                Set ASY1/2 pattern
sdpt                Set DAC pattern
srcu                Sets new RCU
scap                Starts a RCU symbols capture
sret                Affects parameter to "condret"
scon                Affects "condret" to "condcode"
rest                Restart

------------------  UI event's parsing --------------------------------------
ev_opt              Convert EV_... to OPT...
ev_asy0_rcv         EV_ASY0_RCV     has been received - Determine command
ev_asy0_snd         EV_ASY0_SND     has been received - Determine command
ev_asy12_rcv        EV_ASY1/2_RCV   has been received - Determine command
ev_asy12_snd        EV_ASY1/2_SND   has been received - Determine command
ev_gpio012_in       EV_GPIO0/1/2_IN has been received - Determine command
ev_i2c_end          EV_I2C_END      has been received - Determine command
ev_kbd_rcv          EV_KBD_RCV      has been received - Determine command
ev_msec             EV_MSEC         has been received - Determine command

------------------  UI waiting events subroutines ---------------------------
wait_myevents       Wait for all my events
*/



/*  Procedure init_app_fsm ----------------------------------------------------

    Purpose : Creates one VMK Finite State Machine (automaton), then sends
              one initialization event to it. This code is executed within
              VMK hardware context/stack. The C treatments of this automaton 
              will be executed with the VMOS hardware context/stack (taskid 0).
*/

int init_app_fsm(Task_grp *g,char *mod, char*conf)
  {
    int             ret              ; // Returned code

/*****************************************************************************
 * Step 1 : Declare our FSM (automaton) to the VMK. There is only one call   *
 * ------   to the "iniaut" procedure to be done and the AUT_USND FSM is     *
 *          immediately "on line". In our case we do not use the             *
 *          hierarchical capabilities of the VMK FSM engine and so we        *
 *          provide 1 for "Depth of state stacks". We also do not use in our *
 *          code the "pushcond" procedure (see vmk1.c 2.7.3 pushcond proced) *
 *          and so we set to HNULL the "acond" input parameter               *
 *****************************************************************************/

    iniaut(AUT_USND   ,                // Automaton number
           trans_usnd ,                // Transition table address
           lim_usnd   ,                // First transition num. for each state
           STATENB    ,                // Transition table size
           VLNB       ,                // External Logical Ways Number
           VLNB       ,                // Internal Logical Ways Number
           1          ,                // Depth of state stacks
           0          ,                // Depth of the conditions stack
           state_usnd ,                // State variables storage address
           sp_usnd    ,                // Stack pointers storage address
           HNULL      ,                // Conditions stack storage address
           &evt_usnd  ,                // Where to copy the incoming event
           &ret         )            ; // Procedure returned error code


    return 0                         ; // Exit without any error
  }


/*  Procedure usnd_start ------------------------------------------------------

    Purpose : SND_START event treatment. We store the received event parameters
              in the "Usnd_ctx" context and we call "myio_send" 2 times.
*/
static int usnd_start(int par)
  {
    Usnd_ctx        *ctx             ; // Context address
    int             i                ; // Loop counter
    int             ret              ; // Returned code
    S_evt           evt              ; // R_SND_START response event

/*****************************************************************************
 * Step 1 : Store the parameters of the SND_START event in the logical way   *
 * ------   context as follows. We are using here two global variables that  *
 *          have been set by the VMK before calling the present procedure.   *
 *          The "voielog" global variable is the value of the current        *
 *          Logical Way. The "evt_usnd" (S_evt structure) contains a copy of *
 *          the just received event (so SND_START here):                     *
 *          - First, we set a pointer "ctx" on the context that is           *
 *            associated to the current LW, to the address of the "voielog"  *
 *            entry of the "usnd_ctx[]" table of context structures.         *
 *          - All the events are containing the reference of the sender of   *
 *            the event. This reference is made of 3 fields "emet" (The      *
 *            automaton number), "evl" (logical way number) and "node" (The  *
 *            node number, only used for a networked spread OS). We keep in  *
 *            the context the values of "emet" and "evl". The SND_START      *
 *            event also contains in the "res2" field the Internal Queue     *
 *            number where a response event should be written. We keep that  *
 *            queue number in "ctx->nfa".                                    *
 *          - The SND_START event contains three parameters, that have been  *
 *            written by "req_snd_start":                                    *
 *            . The "reserve" field contains the IOD of the device that will *
 *              be used to send the data. We store it in "ctx->iod".         *
 *            . The "adresse" field contains the address of the data buffer  *
 *              that will be repeatedly sent. We store it in "ctx->buf".     *
 *            . The "longueur" field contains two packed values. Bits b0..15 *
 *              is the number of data bytes or symbols that are in the       *
 *              buffer, we store that count in "ctx->buflg". With the RCU    *
 *              transmitter, bits b16..31 are the value of the "rpt" param   *
 *              that will be given to "send_rcu", we keep that value in the  *
 *              context "rpt" field.                                         *
 *          - With RCU, we will use a time-out. We have a field context that *
 *            stores the running time-out identifier and the 0xFFFFFFFF      *
 *            empty value while the time-out is not running. We set this     *
 *            "ideto" field to the 0xFFFFFFFF empty value                    *
 *****************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]

    ctx->emet  = evt_usnd.emet       ; // Automaton   number of sender
    ctx->evl   = evt_usnd.evl        ; // Logical Way number of sender
    ctx->nfa   = evt_usnd.res2       ; // Queue number       of sender

    ctx->iod   = evt_usnd.reserve    ; // IOD of device to be used
    ctx->buf   = evt_usnd.adresse    ; // Data buffer address
    ctx->buflg = evt_usnd.longueur     // Number of data bytes (bits with RCU)
               & 0xFFFF              ; // are bits b0..15
    ctx->rpt   = evt_usnd.longueur     // With the RCU transmitter, bits 
               >> 16                 ; // b16..31 is the count of repeats
    ctx->idto  = 0xFFFFFFFF          ; // TO_RCU identifier = empty


/*****************************************************************************
 * Step 2 : Special case for VLSNDRCU. We do not execute step 3 that starts  *
 * ------   two send requests. We just set condition C_RCU because RCU is    *
 *          using a dedicated set of states, starting with the SND_WAIT      *
 *          state. The C_RCU condition will execute the "urcu_send"          *
 *          treatment that requests to start a RCU transmit. The RESP_WRITE  *
 *          end of transmission event will be received in the SND_WAIT state.*
 *          After calling "relaut" we have to jump to step 4 because we need *
 *          to respond with a R_SND_START event.                             *
 *****************************************************************************/

    if (voielog EQ VLSNDRCU)           // If this is a RCU transmitter request,
      BEGIN                            // we set C_RCU that will execute the
        relaut(C_RCU)                ; // "urcu_send" treatment and will go
        goto step4                   ; // to the SNDWAIT next step. Here we
      END_IF                           // jump to step4 to send the R_SND_START
                                       // to the SND_START sender.

/*****************************************************************************
 * Step 3 : We issue two write request, two calls to the "myio_send"         *
 * ------   procedure, using the same data buffer "ctx->buf". The reason to  *
 *          do that is that we want to reduce to 0 the latency between two   *
 *          successive writes started by the driver. Please remind that the  *
 *          drivers we are using have been allocated using the NBLOCKING_IO  *
 *          option bit (see calls that have been made to "drv_alloc_device").*
 *          As a result, the two calls to "myio_send" that we do here are    *
 *          non blocking, each of those just sends a REQ_WRITE event to the  *
 *          VMIO driver and we DO NOT WAIT here for the RESP_WRITE :         *
 *          - We set "ctx->nbreq" to 2, this value is the count of           *
 *            RESP_WRITE event we will be waiting for.                       *
 *          - The first call to "myio_send" sends a REQ_WRITE to the VMIO    *
 *            driver. The sole fact to writing this event in the VMIO        *
 *            automaton engine event input queue interrupts with no latency  *
 *            our code and the VMIO is entered. The VMIO stack is installed. *
 *            The VMIO FSM engine calls a C procedure of the driver, that is *
 *            its treatement for the REQ_WRITE event. As the transmission    *
 *            medium is currently idle, the physical "send" of the data      *
 *            block immediately begins. When the driver C procedure returns, *
 *            the VMIO exits, our stack is restored and we finish the        *
 *            execution of the code of the "myio_send" procedure.            *
 *          - So when the first call to "myio_send" returns, transmission of *
 *            the first byte of the message has been started, and probably   *
 *            not more has been done (second byte transmission probably not  *
 *            yet started).                                                  *
 *          - Then we call "myio_send" for the second time. At that instant, *
 *            for sure transmission of the first data block is ongoing and   *
 *            it will not be completed before a "long" time. The "myio_send" *
 *            sends a second REQ_WRITE that is written in the VMIO event     *
 *            input queue. This event writing immediately interrupts the     *
 *            "myio_send" code, the VMIO stack is installed and the driver's *
 *            REQ_WRITE handling treatment is executed for the second time.  *
 *            But now the first transfer is still ongoing, and so the        *
 *            driver's REQ_WRITE handling treatment will just keep in an     *
 *            internal queue the values of the second REQ_WRITE parameters,  *
 *            including the buffer address and the number of data bytes to   *
 *            be sent.                                                       *
 *         So just after we exit from the second call to "myio_send", the    *
 *         current situtation can be depicted as follows:                    *
 *         - Transmission of the first message is ongoing, and a "long"      *
 *           time will occur before it's completion.                         *
 *         - The second sending request is stored in an internal driver's    *
 *           waiting queue only dedicated to write requests.                 *
 *         And here is the reason for doing those two non-blocking write     *
 *         requests: The hardware will raise an "End Of Transmission"        *
 *         Interrupt Request exactly (or even some time before) when the     *
 *         last byte of the first message has been sent. In our case, the    *
 *         interrupt handling processing is NEVER deferred, and this one     *
 *         IMMEDIATELY start the second message sending. Therfore, latency   *
 *         between transmission of the last byte of the first message and    *
 *         the first byte of the second message cannot be shorter since it   *
 *         is 0. This is true with all the HypOS drivers.                    *
 *****************************************************************************/

    ctx->nbreq = 2                   ; // We are going to do 2 writes

    for(i = 0; i LT 2; i++)            // Send 2 write requests to the VMIO
      myio_send (ctx->iod   ,          // IOD of device
                 ctx->buf   ,          // Buffer Address
                 ctx->buflg ,          // Number of bytes to be sent
                 &ret        )       ; // Count (not used here)


/******************************************************************************
 * Step 4 : Send the R_SND_START response event. The SND_START event has been *
 * -------  sent by "req_snd_start" procedure, that had stored in the event   *
 *          the caller's Internal Queue Number (Value is 2 to 15 and has been *
 *          written in the "res2" event field). Also, the "putevt_vmk"        *
 *          procedure also stores in the "emet" and "evl" fields of the       *
 *          event the automaton and LW numbers of the event's sender. So here *
 *          we can return to the sender a R_SND_START response event. This    *
 *          event is awaited inside the "req_snd_start" procedure.            *
 ******************************************************************************/

    step4                            : // Send the R_SND_START event

    Memset(&evt,0,sizeof(evt))       ; // Clear the event structure

    evt.aut      = ctx->emet         ; // Target automaton number
    evt.vl       = ctx->evl          ; // Target logical way number
    evt.code     = R_SND_START       ; // Event code

    putevt_vmk(ctx->nfa   ,            // VMK Internal queue 2 to 15
               &evt        )         ; // Event to be written

    return 0                         ; // Exit without any error
  }


/*  Procedure usnd_stop -------------------------------------------------------

    Purpose : SND_STOP event treatment. We store in the context the references
              "emet" and "evl" of the one that sent us the SND_STOP event and
              also in "nfa" the Internal Queue number that will be used to
              return the R_SND_STOP response event.
*/

static int usnd_stop(int par)
  {
    Usnd_ctx        *ctx             ; // Context address

/*****************************************************************************
 * Step 1 : Store the reference of the one who sends us the SND_STOP event.  *
 * ------   We will need it later (in usnd_end) to return a R_SND_STOP       *
 *          response event to the SND_STOP event sender:                     *
 *          - First, we set a pointer "ctx" on the context that is           *
 *            associated to the current LW. The VMK, before calling the      *
 *            present procedure, has set the "voielog" global variable to    *
 *            the current LW number. We use it as an index in our context    *
 *            table "usnd_ctx[]" and so "ctx" is set to &usnd_ctx[voielog].  *
 *          - The VMK has also copied in the "evt_usnd" global variable      *
 *            (a S_evt structure) the just received event (so a SND_STOP).   *
 *            we extract from it the sender automaton number "emet", the     *
 *            sender logical way number "evl" and the Internal Queue number  *
 *            "res2" where the R_SND_STOP event will have to be written. All *
 *            those 3 values are needed by the "usnd_end" procedure that     *
 *            will be called later.                                          *
 *****************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]

    ctx->emet  = evt_usnd.emet       ; // Automaton   number of sender
    ctx->evl   = evt_usnd.evl        ; // Logical Way number of sender
    ctx->nfa   = evt_usnd.res2       ; // Queue number       of sender

    return 0                         ; // Exit without any error
  }


/*  Procedure usnd_resp -------------------------------------------------------

    Purpose : RESP_WRITE event treatment. As one transmission has been
              completed, we start a new one, calling "myio_send" one time.
*/
static int usnd_resp(int par)
  {
    Usnd_ctx        *ctx             ; // Context address
    int             ret              ; // Returned code

/*****************************************************************************
 * Step 1 : Decrement by 1 the number of awaited RESP_WRITE event, this      *
 * ------   count being the "nbreq" field of the Logical Way context:        *
 *          - The VMK, before calling this procedure, has set the "voielog"  *
 *            global variable the value to the current Logical Way and so    *
 *            we just use it as an index in the "usnd_ctx[]" context table.  *
 *            So we set "ctx" to &usnd_ctx[voielog].                         *
 *          - Then we decrement by 1 the "ctx->nbreq" counter.               *
 *****************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]

    ctx->nbreq --                    ; // Decrement the number of still
                                       // awaited RESP_WRITE events.

    if ( par ) goto step3            ; // If we are in the STOPPING state


/*****************************************************************************
 * Step 2 : At that point the automaton is in the STARTED state (the state   *
 * ------   variable value is STARTED). The Driver's Interrupt handler has   *
 *          just started transmitting a new frame before sending us the      *
 *          RESP_WRITE for the previous frame. So we have "plenty" of time   *
 *          (The transmit duration of the just started frame) to give to the *
 *          driver another new frame. We call the "myio_send" procedure to   *
 *          do this, we remind that this call is a non-blocking one, it does *
 *          not waits for any RESP_WRITE event. So the situation is as       *
 *          follows:                                                         *
 *          - The driver is currently transmitting the very beginning of the *
 *            frame specified by the previous call to "myio_send".           *
 *          - The "myio_send" procedure we call here puts a new REQ_WRITE    *
 *            event in the VMIO event input queue. The fact of writing the   *
 *            event immediately interrupts the "myio_send" code, the VMIO    *
 *            stack is installed and the driver's REQ_WRITE handling         *
 *            treatment is immediately executed. But currently a frame is    *
 *            being sent, and so the driver's REQ_WRITE handling treatment   *
 *            will just store in an internal queue the values of the new     *
 *            REQ_WRITE parameters, including the buffer address and the     *
 *            number of data bytes to be send. Then we exit from the VMIO,   *
 *            the stack is restored, we resume the execution of the          *
 *            "myio_send" code and then we return from "myio_send".          *
 *          So after returning from "myio_send", the new situation is:       *
 *          - Transmission of the previous frame is "ongoing", we are still  *
 *            near the very beginning of this frame.                         *
 *          - The send request we just did is stored in an internal driver's *
 *            waiting queue that is only dedicated for write requests.       *
 *          We increment by 1 the "ctx->nbreq" counter and its value is now  *
 *          equal to 2. We are done and we exit from the present procedure.  *
 *****************************************************************************/

    myio_send (ctx->iod   ,            // IOD of device
               ctx->buf   ,            // Buffer Address
               ctx->buflg ,            // Number of bytes to be sent
               &ret        )         ; // Counter (not used here)

    ctx->nbreq ++                    ; // Count goes from 1 to 2

    goto end                         ; // we are done


/*****************************************************************************
 * Step 3 : Here, the automaton is in the STOPPING state (the state variable *
 * ------   value is equal to STOPPING). We have decremented the counter of  *
 *          remaining awaited RESP_WRITE. We will go two times through this  *
 *          step. The first time the new counter value is 1 and we do        *
 *          nothing. The second time the new couner value is 0 and we set    *
 *          the C_END condition, thus resulting to an immediate call of      *
 *          "usnd_end" treatment following the exit of the present procedure.*
 *          The "usnd_end" procedure sends a R_SND_STOP event to the one     *
 *          that sent us the SND_STOP request. This R_SND_STOP event is      *
 *          awaited by the "req_snd_stop" procedure that is currently        *
 *          unscheduled.                                                     *
 *****************************************************************************/

    step3                            : // Current state is STOPPING

    if (ctx->nbreq LE 0)               // If no more RESP_WRITE are awaited
      relaut(C_END)                  ; // then execute the "usnd_end"
                                       // treatment

    end                              :

    return 0                         ; // Exit without any error
  }


/*  Procedure usnd_end --------------------------------------------------------

    Purpose : C_END condition treatment. A response event is returned to the
              one that sends us the SND_STOP event.
*/

static int usnd_end(int par)
  {
    Usnd_ctx        *ctx             ; // Context address
    S_evt           evt              ; // Response event structure

/******************************************************************************
 * Step 1 : Set a pointer "ctx" on the context structure for the current      *
 * ------   logical way. The VMK, before calling the present procedure, has   *
 *          set the "voielog" global variable to the current LW number. We    *
 *          use it as an index in our context table "usnd_ctx[]" and so "ctx" *
 *          is set to &usnd_ctx[voielog]                                      *
 ******************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]


/******************************************************************************
 * Step 2 : Send the R_SND_STOP response event. The SND_STOP event has been   *
 * -------  sent by "req_snd_stop" procedure and this one has requested to    *
 *          be unscheduled until a R_SND_STOP event is received. Here, we     *
 *          send that response event. The "usnd_stop" procedure was the       *
 *          treatment of the SND_STOP event and it has stored in the context  *
 *          the information that we need here to build the response event:    *
 *          - The sender automaton/logical way numbers have been stored in    *
 *            the "emet" and "evl" fields of the context.                     *
 *          - The sender's waiting queue number has been written in "nfa".    *
 *          After setting the event's field, we just call "putevt_vmk"        *
 *          procedure. After we return from the present procedure, the VMK    *
 *          will re-schedule the "req_snd_stop" procedure that is waiting for *
 *          this event.                                                       *
 ******************************************************************************/

    Memset(&evt,0,sizeof(evt))       ; // Clear the event structure

    evt.aut      = ctx->emet         ; // Target automaton number
    evt.vl       = ctx->evl          ; // Target logical way number
    evt.code     = R_SND_STOP        ; // Event code

    putevt_vmk(ctx->nfa   ,            // VMK Internal queue 2 to 15
               &evt        )         ; // Event to be written


    return 0                         ; // Exit without any error
  }


/*  Procedure urcu_send -------------------------------------------------------

    Purpose : C_RCU and TO_RCU treatment. We request the RCU system driver to
              start transmitting a new message.
*/

static int urcu_send(int par)
  {
    Usnd_ctx        *ctx             ; // Context address
    int             ret              ; // Error code from send_rcu

/******************************************************************************
 * Step 1 : Set a pointer "ctx" on the context structure for the current      *
 * ------   logical way. The VMK, before calling the present procedure, has   *
 *          set the "voielog" global variable to the current LW number. We    *
 *          use it as an index in our context table "usnd_ctx[]" and so "ctx" *
 *          is set to &usnd_ctx[voielog]                                      *
 ******************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]

    send_rcu( ctx->iod              ,  // Not an IOD but RCU system identifier
         (unsigned short*) ctx->buf ,  // Message to be sent address
         ctx->buflg                 ,  // Size of the message
         ctx->rpt                   ,  // Repeat count for the message
         RESP_WRITE                 ,  // Notification event code
         &ret                      ) ; // Error code

    return 0                         ; // Exit without any error
  }


/*  Procedure urcu_tostart ----------------------------------------------------

    Purpose : RESP_WRITE treatment for RCU. We start a TO_RCU time-out. When
              the end of period notification event will be received, then the
              "urcu_send" treatment will be executed to start next transmission
*/

static int urcu_tostart(int par)
  {
    Usnd_ctx        *ctx             ; // Context address

/******************************************************************************
 * Step 1 : Set a pointer "ctx" on the context structure for the current      *
 * ------   logical way. The VMK, before calling the present procedure, has   *
 *          set the "voielog" global variable to the current LW number. We    *
 *          use it as an index in our context table "usnd_ctx[]" and so "ctx" *
 *          is set to &usnd_ctx[voielog].                                     *
 ******************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]


/******************************************************************************
 * Step 2 : We start the timer and keep its identifier in the "ctx->idto"     *
 * ------   field context. The reason to keep this identifier is because in   *
 *          the event we would clear the time-out, the "clear_uto" needs it.  *
 *          Otherwise we would not need to keep it.                           *
 *          ----------------------------------------------------------------- *
 *          The VMK uses NFA_STD_PS as the internal VMK queue for ALL the     *
 *          FSM's, therefore we must use NFA_STD_PS as the third parameter    *
 *          to the "set_to" procedure.                                        *
 ******************************************************************************/

    set_to(TIMER            ,         // One shot timer
           5000             ,         // Timer duration in milliseconds
           NFA_STD_PS       ,         // Wait queue for the expiration event
           TO_RCU           ,         // End of period notification event code
           0                ,         // Reserve field for notification event
           AUT_USND         ,         // FSM number for notification event
           voielog          ,         // Logical way for notification event
           (int*) &ctx->idto  )     ; // Returned time-out identifier

    return 0                         ; // Exit without any error
  }


/*  Procedure urcu_stop -------------------------------------------------------

    Purpose : SND_STOP treatment for RCU. This treatment is executed when we
              have a running time-out, so when we wait before sending the next
              RCU message. We just clear the running time-out and then we send
              the R_SND_STOP response event.
*/

static int urcu_stop(int par)
  {
    Usnd_ctx        *ctx             ; // Context address
    S_evt           evt              ; // Response event structure

/******************************************************************************
 * Step 1 : Set a pointer "ctx" on the context structure for the current      *
 * ------   logical way. The VMK, before calling the present procedure, has   *
 *          set the "voielog" global variable to the current LW number. We    *
 *          use it as an index in our context table "usnd_ctx[]" and so "ctx" *
 *          is set to &usnd_ctx[voielog]                                      *
 ******************************************************************************/

    ctx        = usnd_ctx              // We set "ctx" to the address of
               + voielog             ; // usnd_ctx[voielog]

/******************************************************************************
 * Step 2 : Clear the runnning time-out. The time-out identifier has been     *
 * ------   stored in "ctx->idto" by the "urcu_tostart" treatment, we just    *
 *          have to call the "clear_uto" procedure                            *
 ******************************************************************************/

    clear_to(ctx->idto ,               // Time-out identifier
             AUT_RCU   ,               // Time-out destination FSM
             voielog    )            ; // Time-out destination LW


/******************************************************************************
 * Step 3 : Send the R_SND_STOP response event. The SND_STOP event has been   *
 * -------  sent by "req_snd_stop" procedure and this one has requested to be *
 *          unscheduled until a R_SND_STOP event is received. Here, we send   *
 *          that response event. The response to the received SND_STOP event  *
 *          is built as follows:                                              *
 *          - The "aut" destination is "evt_usnd.emet"                        *
 *          - The "vl"  destination is "evt_usnd.evl"                         *
 *          - The "code" response event is R_SND_STOP                         *
 *          The queue number to put the event response in is "evt_usnd.res2". *
 *          After setting the event's field, we just call "putevt_vmk"        *
 *          procedure. After we return from the present procedure, the VMK    *
 *          will re-schedule the "req_snd_stop" procedure that is waiting for *
 *          this event.                                                       *
 ******************************************************************************/

    Memset(&evt,0,sizeof(evt))       ; // Clear the event structure

    evt.aut      = evt_usnd.emet     ; // Target automaton number
    evt.vl       = evt_usnd.evl      ; // Target logical way number
    evt.code     = R_SND_STOP        ; // Event code

    putevt_vmk(evt_usnd.res2 ,         // VMK Internal queue 2 to 15
               &evt            )     ; // Event to be written


    return 0                         ; // Exit without any error
  }


/*  Procedure req_snd_start ---------------------------------------------------

    Purpose : Subroutine to send a SND_START event to the AUT_USND automaton
              and then wait for the R_SND_START response event. This procedure
              is the API to the FSM and is intended to be used from the user
              task code.
*/

static void req_snd_start(int vl, unsigned int iod, unsigned char *buf, int lg)
  {
    S_evt           evt              ; // Response event structure

/*****************************************************************************
 * Step 1 : Initialize the "evt" structure fields:                           *
 * ------   - Set the event destination, that is made of the automaton       *
 *            destination number AUT_USND and the logical way destination    *
 *            number "vl".                                                   *
 *          - We have are arbitrally chosen to use the "res2" field to       *
 *            provide to AUT_USND our own internal queue number. The         *
 *            "usnd_start" treatement of AUT_USND copies the "res2" field    *
 *            in the "nfa" field of its "Usnd_ctx" context and then this     *
 *            value will be used by the AUT_USND C treatments as the first   *
 *            parameter to the "putevt_vmk" function. We do not have to call *
 *            any API procedure to get our own Internal Queue Number since   *
 *            the VMK provides us a global variable "numfa" that holds that  *
 *            value. So we just have to set "evt.res2" to "numfa" value.     *
 *          - The event's code is SND_START. Indeed, this value MUST exist   *
 *            in the AUT_USND transtion table (trans_usnd[]).                *
 *          - The other values we want to communicate as parameters to the   *
 *            AUT_USND FSM are the iod to be used to send data, the buffer   *
 *            to be sent and the count of bytes of the buffer. We            *
 *            respectively use the fields "reserve", "adresse" and           *
 *            "longueur" of the event to convey those.                       *
 *          ---------------------------------------------------------------- *
 *          The parsing of this event is done within "usnd_start", that      *
 *          stores the parameters in the proper "Usnd_ctx" context structure.*
 *****************************************************************************/

    Memset(&evt,0,sizeof(evt))       ; // Clear the event structure

    evt.aut      = AUT_USND          ; // Target automaton number
    evt.vl       = vl                ; // Target logical way number
    evt.res2     = numfa             ; // Our Internal Queue number

    evt.code     = SND_START         ; // Event code
    evt.reserve  = iod               ; // reserve = iod
    evt.adresse  = buf               ; // adresse = telecom buffer address
    evt.longueur = lg                ; // lg      = Number of bytes in buffer
                                       // lg      = rpt<<16 + nbsymb


/*****************************************************************************
 * Step 2 : Write in VMK internal waiting queue the event for the FSM.       *
 * ------   The VMK uses NFA_STD_PS as the internal VMK queue for ALL the    *
 *          FSM's, therefore we must use NFA_STD_PS as the first parameter   *
 *          to the "putevt_vmk" procedure.                                   *
 *****************************************************************************/

    putevt_vmk(NFA_STD_PS ,            // VMK Internal queue 17
               &evt        )         ; // Event to be written


/*****************************************************************************
 * Step 3 : Wait for the R_SND_START response event send by AUT_USND.        *
 * ------   We arbitrarily have chosen that AUT_USND responds to SND_START   *
 *          by a R_SND_START response event (see procedure "usnd_start"). So *
 *          here we unschedule our task until we receive R_SND_START. We use *
 *          the "wait_coderes" subroutine to unschedule.                     *
 *****************************************************************************/

    wait_coderes(R_SND_START,0)      ; // Unschedule until R_SND_START is
                                       // received.
  }


/*  Procedure req_snd_stop ----------------------------------------------------

    Purpose : Subroutine to send a SND_STOP event to the AUT_USND automaton
              and to wait for the response event
*/

static void req_snd_stop(int vl)
  {
    S_evt           evt              ; // Response event structure

/*****************************************************************************
 * Step 1 : Initialize the "evt" structure fields:                           *
 * ------   - Set the event destination, that is made of the automaton       *
 *            destination number AUT_USND and the logical way destination    *
 *            number "vl".                                                   *
 *          - We have are arbitrally chosen to use the "res2" field to       *
 *            provide to AUT_USND our own internal queue number. The         *
 *            "usnd_stop" treatement of AUT_USND copies the "res2" field     *
 *            in the "nfa" field of its "Usnd_ctx" context and then this     *
 *            value will be used by another AUT_USND C treatment (usnd_end)  *
 *            as the first parameter to the "putevt_vmk" function. We do not *
 *            have to call any API procedure to get our own Internal Queue   *
 *            Number since the VMK provides us a global variable "numfa"     *
 *            that holds that value. So we just have to set "evt.res2" to    *
 *            "numfa" value.                                                 *
 *          - The event's code is SND_STOP. Indeed, this value MUST exist    *
 *            in the AUT_USND transtion table (trans_usnd[]).                *
 *          ---------------------------------------------------------------- *
 *          The parsing of this event is done within "usnd_stop", that       *
 *          stores the parameters in the proper "Usnd_ctx" context structure.*
 *          The response event R_SND_STOP will be send by "usnd_end".        *
 *          ---------------------------------------------------------------- *
 *          One can wonder why we again send to AUT_USND our internal queue  *
 *          number since we already sent a queue number to AUT_USND along    *
 *          with the SND_START event. The reason to do this is that we do    *
 *          not impose that SND_START and SND_STOP to be sent by the same    *
 *          task, even it is the case with this sample code.                 *
 *****************************************************************************/

    Memset(&evt,0,sizeof(evt))       ; // Clear the event structure

    evt.aut      = AUT_USND          ; // Target automaton number
    evt.vl       = vl                ; // Target logical way number
    evt.res2     = numfa             ; // Our Internal Queue number

    evt.code     = SND_STOP          ; // Event code


/*****************************************************************************
 * Step 2 : Write in VMK internal waiting queue the event for the FSM.       *
 * ------   The VMK uses NFA_STD_PS as the internal VMK queue for ALL the    *
 *          FSM's, therefore we must use NFA_STD_PS as the first parameter   *
 *          to the "putevt_vmk" procedure.                                   *
 *****************************************************************************/

    putevt_vmk(NFA_STD_PS ,            // VMK Internal queue 17
               &evt        )         ; // Event to be written


/*****************************************************************************
 * Step 3 : Wait for the R_SND_STOP response event send by AUT_USND.         *
 * ------   We arbitrarily have chosen that AUT_USND responds to SND_STOP    *
 *          by a R_SND_STOP response event (see procedure "usnd_end"). So    *
 *          here we have to unschedule our task until we receive the         *
 *          R_SND_STOP response event. We use the "wait_coderes" subroutine  *
 *          to unschedule.                                                   *
 *****************************************************************************/

    wait_coderes(R_SND_STOP,0)       ; // Unschedule until T_SND_STOP is
                                       // received
  }


/*  Procedure loop_app_tsk ----------------------------------------------------

    Purpose : This is our task main loop.
*/

int loop_app_tsk(void *param)
  {
    const M_trans   *tra             ; // Transition table line address
    int             ret              ; // Treatement error code

/*****************************************************************************
 * Step 1 : Here we initialize the needed drivers:                           *
 * ------   - The ASY driver, we are using here two serial port contollers.  *
 *          - The GPIO driver, we are using one GPIO controller.             *
 *          - The LED driver, we are using one LED controller.               *
 *          - The keyboard interface KBDITF.                                 *
 *          - The DAC driver, we are using one converter.                    *
 *          We also give the first receive token to ASY\DEV0 that we are     *
 *          using as a "console". The menu is sent to ASY\DEV0 and keyboard  *
 *          characters are received from ASY\DEV0. We also give 2 tokens to  *
 *          the other ASY\DEV1 and ASY\DEV2 lines because we accept all      *
 *          incoming data. When the AUT_USND FSM is idle, the present task   *
 *          echoes on ASY all that is received on this same line. When the   *
 *          AUT_USND is running for ASY1/ASY2, then the present task just    *
 *          discards the data received on ASY1/ASY2 (see "ev_asy_rcv"        *
 *          procedure).                                                      *
 *****************************************************************************/

    start                            : // Label if "restart" is requested

    open_asy()                       ; // Open the three serial ports
    open_gpio()                      ; // Open the GPIO driver
    open_led()                       ; // Open the LED driver
    open_kbd()                       ; // Open the KBDITF driver
    open_dac()                       ; // Open the DAC driver

    myio_givetok(asy_iod0  ,           // I/O descriptor
                 2         ,           // Number of receive token
                 1         ,           // Size of receive buffer
                 &tok_rcv[0] )       ; // Counter of given tokens

    myio_givetok(asy_iod1 ,            // I/O descriptor
                 2        ,            // Number of receive token
                 LGRCV12  ,            // Size of receive buffer
                 &tok_rcv[1])        ; // Counter of given tokens

    myio_givetok(asy_iod2 ,            // I/O descriptor
                 2        ,            // Number of receive token
                 LGRCV12  ,            // Size of receive buffer
                 &tok_rcv[2])        ; // Counter of given tokens


/*****************************************************************************
 * Step 2 : The menus allow the user to modify 19 values that are the 19     *
 * ------   integers of the "cfg[19]" global variable. Here we affect        *
 *          initial values to those ones. One has to be aware that the       *
 *          "open_xyz" procedures are doing "set_vals" with default string   *
 *          tags (asy_taglist0, asy_taglist1, gpio_tag_list) and the values  *
 *          we affect here must be the same ones are the ones defined by the *
 *          tag lists, or the values displayed on the UI will not be the     *
 *          ones effectively used by the drivers.                            *
 *****************************************************************************/

    cfg [VRED   ] = CFG_STOPPED      ; // Pattern on RED   : stopped
    cfg [VGREEN ] = CFG_STOPPED      ; // Pattern on GREEN : stopped
    cfg [VYELLO ] = CFG_STOPPED      ; // Pattern on YELLOW: stopped
    cfg [VASY1  ] = CFG_STOPPED      ; // Serial ASY1 transmit: stopped
    cfg [VASY2  ] = CFG_STOPPED      ; // Serial ASY2 transmit: stopped
    cfg [VDAC   ] = CFG_STOPPED      ; // DAC transmit: stopped
    cfg [VUHFRCU] = CFG_STOPPED      ; // UHF transmit: stopped

    cfg [VASY1PT] = 0                ; // ASY1 transmit pattern 0x00 -> 0xFF
    cfg [VASY2PT] = 1                ; // ASY2 transmit pattern 0xFF -> 0x00
    cfg [VDACPT ] = 0                ; // DAC  transmit pattern

    cfg [VBAUD1 ] = CFG_9600         ; // Baudrate: 9600 bauds
    cfg [VBITS1 ] = CFG_8BITS        ; // Nb of bits/sym: 8 bits/sym
    cfg [VSTOP1 ] = CFG_1STOP        ; // Nb of stop bits: 1 stop bit
    cfg [VPAR1  ] = CFG_NOPARITY     ; // Parity: none
    cfg [VDMA1  ] = CFG_DMAON        ; // DMA: ON      
    cfg [VFLOW1 ] = CFG_NOFLOWCTL    ; // Flow control: none
    cfg [VFLUSH1] = CFG_FULLFLUSH    ; // Timing config: none
    cfg [VTEMPO1] = CFG_1MS          ; // Timing: 1 ms
    cfg [VSPEED1] = CFG_FAST         ; // Speed: normal

    cfg [VBAUD2 ] = CFG_9600         ; // Baudrate: 9600 bauds
    cfg [VBITS2 ] = CFG_8BITS        ; // Nb of bits/sym: 8 bits/sym
    cfg [VSTOP2 ] = CFG_1STOP        ; // Nb of stop bits: 1 stop bit
    cfg [VPAR2  ] = CFG_NOPARITY     ; // Parity: none
    cfg [VDMA2  ] = CFG_DMAON        ; // DMA: ON      
    cfg [VFLOW2 ] = CFG_NOFLOWCTL    ; // Flow control: none
    cfg [VFLUSH2] = CFG_FULLFLUSH    ; // Timing config: none
    cfg [VTEMPO2] = CFG_1MS          ; // Timing: 1 ms
    cfg [VSPEED2] = CFG_FAST         ; // Speed: normal

    cfg [VPTIM  ] = CFG_P50MS        ; // Software polling interval: 50 msec
    cfg [VPCNT  ] = CFG_P3           ; // Software polling count: 3 times
    cfg [VMON1  ] = CFG_POLL         ; // Monitoring method: polling

    cfg [VDTIM1 ] = CFG_D50US        ; // Hardware polling interval: 50 usec
    cfg [VDCNT1 ] = CFG_D3           ; // Hardware polling count: 3 times
    cfg [VMON2  ] = CFG_POLL         ; // Monitoring method: polling
    cfg [VDTIM2 ] = CFG_D50US        ; // Hardware polling interval: 50 usec
    cfg [VDCNT2 ] = CFG_D3           ; // Hardware polling count: 3 times
    cfg [VMON3  ] = CFG_POLL         ; // Monitoring method: polling
    cfg [VDTIM3 ] = CFG_D50US        ; // Hardware polling interval: 50 usec
    cfg [VDCNT3 ] = CFG_D3           ; // Hardware polling count: 3 times

    cfg [VINPUT ] = 0                ; // RCU receive input : IR input
    cfg [VNUMRCU] = 0                ; // RCU choice name
    cfg [VONMIN ] = 8000             ; // Leader code : ON minimal usec
    cfg [VONMAX ] = 10000            ; // Leader code : ON maximal usec
    cfg [VSTMIN ] = 12000            ; // Leader code : SYMB minimal usec
    cfg [VSTMAX ] = 15000            ; // Leader code : SYMB maximal usec

/*****************************************************************************
 * Step 3 : The "sval" subroutine is the UI treatment procedure that calls   *
 * ------   the driver "setval" API (it calls "myio_setval" that calls the   *
 *          "drv_setval" OS function). The "sval" treatment takes as input   *
 *          ONE parameter "n" that is an index is "cfg[]". The string        *
 *          character, the tag list to be given to "myio_setval" is simply   *
 *          "str_tag[n][ cfg[n] ]. The "str_tag|n]" is a list of string      *
 *          addresses and we use "cfg[0]". (0,1,...) is the selector for one *
 *          of the strings. For example with n=VBITS, then "str_tag[VBITS]"  *
 *          is the address of "tag_bits" and then if for example "cfg[VBITS]"*
 *          is 1 then "str_tag[1] is the address of "NBBITS=7", that will be *
 *          given to "myio_setval". But the "myio_setval" procedure also     *
 *          needs another parameter that is an IOD. It has to be noted that  *
 *          IODs are not predefined values but are returned by the           *
 *          "myio_open" or the "myio_alloc_sub" procedure. So here we set in *
 *          the proper "iods[]" entry (in iods[n]") the needed IOD value.    *
 *          So in the end, the code of "sval(n)" is simply the instruction   *
 *          "myio_setval( iods[n] , str_tag[n][ cfg[n] ] ).                  *
 *****************************************************************************/

    iods[VRED   ] = led_iod0         ; // Pattern on RED  : IOD 
    iods[VGREEN ] = led_iod1         ; // Pattern on GREEN: IOD 
    iods[VYELLO ] = led_iod2         ; // Pattern on GREEN: IOD 
    iods[VASY1  ] = asy_iod1         ; // Start/stop ASY1 : IOD for AUT_USND
    iods[VASY2  ] = asy_iod2         ; // Start/stop ASY1 : IOD for AUT_USND
    iods[VDAC   ] = 0                ; // Start/stop DAC  : IOD
    iods[VUHFRCU] = rcusnd_id        ; // Start/stop RCU  : RCU id

    iods[VBAUD1 ] = asy_iod1         ; // Baudrate: 
    iods[VBITS1 ] = asy_iod1         ; // Nb of bits/sym:
    iods[VSTOP1 ] = asy_iod1         ; // Nb of stop bits:
    iods[VPAR1  ] = asy_iod1         ; // Parity:
    iods[VDMA1  ] = asy_iod1         ; // DMA:
    iods[VFLOW1 ] = asy_iod1         ; // Flow control:
    iods[VFLUSH1] = asy_iod1         ; // Timing config:
    iods[VTEMPO1] = asy_iod1         ; // Timing:
    iods[VSPEED1] = 0                ; // Speed: no IOD

    iods[VBAUD2 ] = asy_iod2         ; // Baudrate: 
    iods[VBITS2 ] = asy_iod2         ; // Nb of bits/sym:
    iods[VSTOP2 ] = asy_iod2         ; // Nb of stop bits:
    iods[VPAR2  ] = asy_iod2         ; // Parity:
    iods[VDMA2  ] = asy_iod2         ; // DMA:
    iods[VFLOW2 ] = asy_iod2         ; // Flow control:
    iods[VFLUSH2] = asy_iod2         ; // Timing config:
    iods[VTEMPO2] = asy_iod2         ; // Timing:
    iods[VSPEED2] = 0                ; // Speed: no IOD

    iods[VPTIM  ] = gpio_iod         ; // Software polling rate: controller
    iods[VPCNT  ] = gpio_iod         ; // Software polling count: controller

    iods[VMON1  ] = gpio_iod1        ; // Monitoring method: sub-channel
    iods[VDTIM1 ] = gpio_iod1        ; // hardware polling rate: sub-channel
    iods[VDCNT1 ] = gpio_iod1        ; // hardware polling count: sub-channel
    iods[VMON2  ] = gpio_iod2        ; // Monitoring method: sub-channel
    iods[VDTIM2 ] = gpio_iod2        ; // hardware polling rate: sub-channel
    iods[VDCNT2 ] = gpio_iod2        ; // hardware polling count: sub-channel
    iods[VMON3  ] = gpio_iod3        ; // Monitoring method: sub-channel
    iods[VDTIM3 ] = gpio_iod3        ; // hardware polling rate: sub-channel
    iods[VDCNT3 ] = gpio_iod3        ; // hardware polling count: sub-channel


/*****************************************************************************
 * Step 4 : Here we draw the first menu, that is MMAIN. We call the "pmen"   *
 * ------   procedure with "MMAIN" value as a parameter. This procedure      *
 *          send the "mmain" const string. Then "pmen" calls the "ptim"      *
 *          subroutine that reads the time, convert it to an ASCII string    *
 *          and sends it on ASY0. But the menu also shows the 7 states of    *
 *          "cfg[0] to "cfg[6]". To do that we use "pgrp(GMAIN") that        *
 *          converts the 7 integer values in corresponding strings and then  *
 *          send on ASY0 this text.                                          *
 *****************************************************************************/

    pmen(MMAIN)                      ; // Print menu on serial line ASY\DEV0
    pgrp(GMAIN)                      ; // Print variable group on ASY\DEV0


/*****************************************************************************
 * Step 5 : We start a timer that will send us an event every 1 second so    *
 * ------   that we can update the time. As the timer is started in CLOCK    *
 *          mode, we will not have to call again "set_tto".                  *
 *****************************************************************************/

    set_tto(CLOCK      ,               // Timer mode: clock
            1000       ,               // Duration in milliseconds
            TICK       ,               // Event code
            0          ,               // Event reserve field
            &idto        )           ; // Timer identifier


/*****************************************************************************
 * Step 6 : Here is our main event loop. For each loop, we do as follows:    *
 * -------  - First we wait for an event (call "wait_myevents").             *
 *          - Then we call the "ev_opt" procedure that will convert the      *
 *            "ev_val" value to another value that exists in the "evtcode"   *
 *            field of the menu transition table.                            *
 *          - Then we scan the "m_trans[page]" (list of transitions for the  *
 *            page number "state") in order to find the transition for the   *
 *            "opt_val" event code. The transition "evtcode" field must be   *
 *            equal to "opt_val". If no transition matches "opt_val" then    *
 *            the scan will stop with the last transition that is indicated  *
 *            with a DEFAULT value in "evtcode".                             *
 *          - The "tra" pointer is now on the found transition (that may be  *
 *            the DEFAULT, the last one. We just call in order the 5         *
 *            procedures that are pointed by "tra->trt1", "tra->trt2",       *
 *            "tra->trt3", "tra->trt4" and "tra->trt5".                      *
 *          - After executing the 5 treatments, we test if any of those has  *
 *            set a non 0 value in the "condcode" global variable. If yes    *
 *            this is our FSM engine condition, we set the "opt_val"         *
 *            variable with "condcode" and we jump to scan again the         *
 *            transition table, searching for a code event equal to          *
 *            "codcode".                                                     *
 *          - If one of the 5 treatments is the "rest" procedure then its    *
 *            set to 1 the "flag_rest" procedure and we stop the loop.       *
 *          ---------------------------------------------------------------- *
 *          One can wonder how the "page" variable is updated. The answer is *
 *          that each time the "pmen" procedure is called its "page" sets    *
 *          to its input parameter.                                          *
 *****************************************************************************/

    errcode   = 0                    ; // Init of global error code
    flag_rest = 0                    ; // Init of the end-of-loop flag. It is
                                       // set to 1 by "rest" (if called)

    wait_ev                          : // Start of our event loop
                                       // -------------------------------------
    ev_val  = wait_myevents()        ; // Unschedule until one event is
                                       // received

    opt_val = ev_opt( ev_val )       ; // Compute a value that can be found
                                       // in the "M_trans" structures

    scan                             : // Parse the transition table
    condcode = 0                     ; // Reset the condition code
                                       //
    for (tra = m_trans[page]      ;    // Scan the "m_trans[page]" list of
         tra->evtcode != opt_val &&    // transition until we found the line
         tra->evtcode != DEFAULT  ;    // which "evtcode" is "opt_val" or
         tra ++                    ) ; // the DEFAULT (last) line of the list.

    if( (ret = tra->trt1(tra->p1)) )   // Execute p1 
      goto error                     ; // and stop if we have an error

    if( (ret = tra->trt2(tra->p2)) )   // Execute p2
      goto error                     ; // and stop if we have an error

    if( (ret = tra->trt3(tra->p3)) )   // Execute p3
      goto error                     ; // and stop if we have an error

    if( (ret = tra->trt4(tra->p4)) )   // Execute p4
      goto error                     ; // and stop if we have an error

    if( (ret = tra->trt5(tra->p5)) )   // Execute p5
      goto error                     ; // and stop if we have an error

    if ( condcode )                    // If any of the treatment as set a
      {                                // non 0 value in the "codecode"
        opt_val = condcode           ; // global variable, this is our FSM
        goto scan                    ; // condition, the condition becomes
      }                                // the new event code and we do another
                                       // transition

    if (flag_rest == 0) goto wait_ev ; // Goto wait for the next event or
    else                goto end     ; // stop if "pres" has set "flag_rest"

    error                            : // In case a treatment returned any
    errcode = ret                    ; // error code, we store it in the
    opt_val = OPT_ERROR              ; // "errocode" global variable, we set
    goto scan                        ; // a new OPT_ERROR event code and we
                                       // do another transition with OPT_ERROR


/*****************************************************************************
 * Step 7 : Close the five drivers, ASY, GPIO, LED, DRVITF and DAC           *
 * ------                                                                    *
 *****************************************************************************/

    end                              :

    clear_tto(idto)                  ; // Stop the timer

    if ( cfg[VASY1] )                  // If ASY1 is transmitting, send a
      req_snd_stop(VLASY1)           ; // STOP request to (AUT_USND,VLASY1)

    if ( cfg[VASY2] )                  // If ASY2 is transmitting, send a
      req_snd_stop(VLASY2)           ; // STOP request to (AUT_USND,VLASY2)

#ifdef TOTO
    if ( cfg[VDAC] )                   // If DAC is sending data, send a
      req_snd_stop(VLDAC)            ; // SND_STOP event to (AUT_USND,VLDAC)
#endif

    if ( cfg[VUHFRCU] )                // If RCU is transmitting, send a
      req_snd_stop(VLSNDRCU)         ; // STOP request to (AUT_USND,VLSNDRCU)

    close_asy()                      ; // Close the two serial ports
    close_gpio()                     ; // Close the GPIO driver
    close_led()                      ; // Close the LED driver
    close_kbd()                      ; // Close the KBDITF driver
    close_dac()                      ; // Close the DAC driver

    goto start                       ;

    return 0                         ;
  }


/*  Procedure open_asy --------------------------------------------------------

    Purpose : Initialize the code of the ASY driver and then opens the three
              serial port that are available. The two opened devices
              descriptors (Input/Output Descriptors") are kept in "asy_iod0" 
              "asy_iod1" and "asy_iod2" respectively. Line parameters (speed, 
              nbbits and parity) are set. Then we allocate for each controller 
              one transmit buffer.
*/

static void open_asy(void)
  {
    int             ret              ; // Procedures returned code

/*****************************************************************************
 * Step 1 : Open the three devices ASY\DEV0 DEV1 and DEV2. We call the       *
 * -------  "myio_open" simple API procedure. In fact the subroutine calls   *
 *          "drv_init_driver", "add_uroute", "drv_alloc_device", and then    *
 *          "drv_open_device".                                               *
 *****************************************************************************/

    myio_open(DRVASY,DEV0,"",&asy_iod0) ; // Open "ASY\DEV0"
    myio_open(DRVASY,DEV1,"",&asy_iod1) ; // Open "ASY\DEV1"
    myio_open(DRVASY,DEV2,"",&asy_iod2) ; // Open "ASY\DEV2"


/*****************************************************************************
 * Step 2 : Set line parameters for the two serial line controllers. We have *
 * ------   to call the "myio_setval" procedure. This procedure sends a      *
 *          REQ_SETVAL request event the AUT_ASY VMIO automaton. Then we     *
 *          unschedule until the RESP_SETVAL response event is received. We  *
 *          do not use the same parameters with the UI ASY0 asynchronous     *
 *          line and the AUT_USND ASY1 line. We use asy_taglist0 with ASY0:  *
 *          "BAUDS=115200\nNBBITS=8\nSTOPBIT=1\nPARITY=NONE". With ASY1 and  *
 *          ASY2 we use taglist1: "BAUDS=9600\nNBBITS=8\nSTOPBIT=1\n"        *
 *          "PARITY=NONE\nBUFSIZE=0\nFLOWCTL=NONE\nTMODE=NONE\nTEMPO=1".     *
 *****************************************************************************/

    myio_setval(asy_iod0,asy_taglist0)  ; // Set "ASY\DEV0" tags
    myio_setval(asy_iod1,asy_taglist1)  ; // Set "ASY\DEV1" tags
    myio_setval(asy_iod2,asy_taglist1)  ; // Set "ASY\DEV2" tags


/*****************************************************************************
 * Step 3 : Allocates memory buffers for transmission, one for DEV0 and two  *
 * ------   other ones for DEV1/DEV2. We store buffers addresses in          *
 *          "buf_snd[0]" and "buf_snd[1/2]". It has to be noted that we do   *
 *          not allocate any receive buffer, because this is done by the ASY *
 *          driver. When the VMIO AUT_ASY receives a REQ_REQ, this event     *
 *          contains a number of allowed receive tokens. The receive buffer  *
 *          allocation is done by AUT_ASY. So the present module does not    *
 *          contains any call to "alloc_buf" regarding receive buffers.      *
 *****************************************************************************/

    alloc_buf(myapp_memuid ,           // Memory user id
              1500         ,           // Size in bytes of requested buffer
              0            ,           // Flags
              &buf_snd[0]  ,           // Address of allocated buffer
              &ret          )        ; // Return code

    alloc_buf(myapp_memuid ,           // Memory user id
              256          ,           // Size in bytes of requested buffer
              0            ,           // Flags
              &buf_snd[1]  ,           // Address of allocated buffer
              &ret          )        ; // Return code

    alloc_buf(myapp_memuid ,           // Memory user id
              256          ,           // Size in bytes of requested buffer
              0            ,           // Flags
              &buf_snd[2]  ,           // Address of allocated buffer
              &ret          )        ; // Return code


/*****************************************************************************
 * Step 4 : AUT_USND will send a continuous byte stream through ASY\DEV1 or  *
 * ------   ASY\DEV2 when it is enabled, to test throughput. We initialize   *
 *          the send buffer once for all with bytes from 0xFF to 0x00.       *
 *****************************************************************************/

    fill256(buf_snd[1],cfg[VASY1PT]) ; // Fill up the 256 bytes send buffer
    fill256(buf_snd[2],cfg[VASY2PT]) ; // Fill up the 256 bytes send buffer
  }


/*  Procedure close_asy -------------------------------------------------------

    Purpose : This procedure closes the three USART, then frees those three
              devices, terminates the ASY module, deletes the route that has 
              been created for the IND_REPORT events, and finally frees the 3 
              telecom buffers that were allocated by "open_asy".
*/

static void close_asy(void)
  {

    int             ret              ; // Return code

/*****************************************************************************
 * Step 1 : We call the "myio_close" procedure that calls "drv_close_device",*
 * ------   "drv_free", "drv_end" and "del_uroute".                          *
 *****************************************************************************/

    myio_close(asy_iod0)             ; // Closes "ASY\DEV0"
    myio_close(asy_iod1)             ; // Closes "ASY\DEV1"
    myio_close(asy_iod2)             ; // Closes "ASY\DEV2"


/*****************************************************************************
 * Step 2 : Free the 3 memory buffers that were allocated by "open_asy".     *
 * ------   For each of them, we just call the "free_buffer" procedure       *
 *****************************************************************************/

    free_buf(buf_snd[0], &ret )      ; // Free "buf_snd[0]"
    free_buf(buf_snd[1], &ret )      ; // Free "buf_snd[1]"
    free_buf(buf_snd[2], &ret )      ; // Free "buf_snd[2]"

    buf_snd[0] = buf_snd[1] =          // Clear references that are
    buf_snd[2] =              HNULL  ; // now invalid.
  }


/*  Procedure open_gpio -------------------------------------------------------

    Purpose : Initialize the code of the GPIO driver module, then allocates
              the GPIO controller device, initializes its hardware and finally
              creates a user route in order to catch the IND_REPORT events.
              The input/output descriptor is kept in the global variable 
              "gpio_iod". 
*/

static void open_gpio(void)
  {

/*****************************************************************************
 * Step 1 : We call the "myio_open" that calls "drv_init_driver", then       *
 * ------   "add_uroute", then "drv_alloc_device" and "drv_open_device".     *
 *****************************************************************************/

    myio_open(DRVGPIO,DEV0,"",&gpio_iod); // Open GPIO\DEV0


/*****************************************************************************
 * Step 2 : The GPIO contoller is ready. We are going to allocate the pins   *
 * ------   subchannels, one for each button. We call the "drv_alloc_subchan"*
 *          procedure, this procedure sends a REQ_ALLOC_SUB event to the     *
 *          VMIO AUT_GPIO automaton. This automaton will then send us a      *
 *          RESP_ALLOC_SUB response event. Here we unschedule until this     *
 *          event is received. Buttons 1, 2, 3 have respectively connected   *
 *          to GPIO named  BUT0, BUT1 and BUT2.                              *
 *****************************************************************************/

    myio_alloc_sub(gpio_iod,"UNAME=BUT1",&gpio_iod1); // Alloc BUT0 GPIO
    myio_alloc_sub(gpio_iod,"UNAME=BUT2",&gpio_iod2); // Alloc BUT1 GPIO
    myio_alloc_sub(gpio_iod,"UNAME=BUT3",&gpio_iod3); // Alloc BUT2 GPIO


/*****************************************************************************
 * Step 3 : The pins are allocated. We need to configure the buttons to get  *
 * ------   IND_REPORT events on state change. We call the "myio_setval"     *
 *          procedure, this procedure sends a REQ_SETVAL event to the VMIO   *
 *          AUT_GPIO automaton. This automaton will then send us a           *
 *          RESP_SETVAL response event. Here we unschedule until this event  *
 *          is received. We do this for buttons 1, 2 and 3, so using the     *
 *          three sub-channels IOD, "gpio_iod1", "gpio_iod2" and "gpio_iod3".*
 *          We use the same tag list for the 3 GPIO's that are connected to  *
 *          "Normally Open" push buttons. Therefore we set a weak pull-up in *
 *          order to maintain the PIO in high state when the button is open. *
 *          When the button is pressed the PIO state is low (GND). So we     *
 *          set the GPIO as an input ("OUTPUT=HI_Z") with pull-up:           *
 *          "FUNC=GPIO\nOUTPUT=HI_Z\nWEAKPULL=UP\nEVENTS=REPORT\n".          *
 *****************************************************************************/

    myio_setval(gpio_iod1,gpio_taglist) ; // Configure BUTO GPIO
    myio_setval(gpio_iod2,gpio_taglist) ; // Configure BUT1 GPIO
    myio_setval(gpio_iod3,gpio_taglist) ; // Configure BUT2 GPIO
  }


/*  Procedure close_gpio ------------------------------------------------------

    Purpose : This procedure closes the GPIO controller, then frees this
              device, terminates the GPIO module and deletes the route that
              had been created for the IND_REPORT events.
*/

static void close_gpio(void)
  {

/*****************************************************************************
 * Step 1 : Free all used subchannels. We call "myio_free_sub" that calls    *
 * ------   "drv_free_subchan" for each of the 3 buttons which IO descriptor *
 *          are "gpio_iod1/2/3". This procedure sends a REQ_FREE_SUB request *
 *          event to the VMIO AUT_GPIO automaton and here we unschedule      *
 *          until the corresponding RESP_FREE_SUB, sent by AUT_GPIO, is      *
 *          received.                                                        *
 *****************************************************************************/

    myio_free_sub(gpio_iod1)         ; // Free BUT1 GPIO
    myio_free_sub(gpio_iod2)         ; // Free BUT2 GPIO
    myio_free_sub(gpio_iod3)         ; // Free BUT3 GPIO


/*****************************************************************************
 * Step 2 : Close the GPIO device. We call the "myio_close" subroutine that  *
 * ------   calls "drv_close" device, the "drv_free" and then "drv_end".     *
 *          A REQ_CLOSE request event is sent to AUT_GPIO and we unschedule  *
 *          until RESP_CLOSE is received from AUT_GPIO. A REQ_FREE event is  *
 *          sent and RESP_FREE is awaited. And a REQ_END driver termination  *
 *          request is sent and we unschedule until RESP_END is received.    *
 *****************************************************************************/

    myio_close(gpio_iod)             ; // Closes the GPIO device and the driver
  }


/*  Procedure open_led --------------------------------------------------------

    Purpose : Initializes the LED driver, opens the LED controller, allocates
              the three LED (RED, GREEN, YELLOW) subchannels.
*/

static void open_led(void)
  {

/*****************************************************************************
 * Step 1 : We call the "myio_open" that calls "drv_init_driver", then       *
 * ------   "add_uroute", then "drv_alloc_device" and "drv_open_device".     *
 *          It has to be noted that the LED driver is a "software device",   *
 *          because there is no LED controller dedicated device. Most of the *
 *          time "hidden" GPIO (not visible by the application) are used.    *
 *          But other hardware may be used. For example a front panel will   *
 *          include a dedicated PCB and an I2C slave controller that drives  *
 *          and controls all the front-panel elements such as LEDS but also  *
 *          buttons, a 4x7 display, ... In such a case, the "drv_led" driver *
 *          will use low-level functions for I2C and not GPIO.               *
 *****************************************************************************/

    myio_open(DRVLED,DEV0,"",&led_iod)  ; // Open LED\DEV0


/*****************************************************************************
 * Step 2 : The contoller is ready. We are going to allocate the LED         *
 * ------   subchannels, one for each LED. We call the "myio_alloc_sub"      *
 *          subroutine that calls the "drv_alloc_subchan" procedure. This    *
 *          last procedure sends a REQ_ALLOC_SUB event to the VMIO AUT_LED   *
 *          automaton. This automaton will then send us a RESP_ALLOC_SUB     *
 *          response event. Here we unschedule until this event is received. *
 *****************************************************************************/

    myio_alloc_sub(led_iod,"LEDNAME=RED"    ,&led_iod0); // Allocates RED
    myio_alloc_sub(led_iod,"LEDNAME=GREEN"  ,&led_iod1); // Allocates GREEN
    myio_alloc_sub(led_iod,"LEDNAME=YELLOW" ,&led_iod2); // Allocates YELLOW
  }


/*  Procedure close_led -------------------------------------------------------

    Purpose : Closes the LED controller, then frees it and terminates the LED
              driver module.
*/

static void close_led(void)
  {

/*****************************************************************************
 * Step 1 : Free all used subchannels. We call "myio_free_sub" that calls    *
 * -------  "drv_free_subchan" for each of the 3 LEDs which IO descriptor    *
 *          are "led_iod0/1/2". This last procedure sends a REQ_FREE_SUB     *
 *          request event to the VMIO AUT_LED automaton and here we          *
 *          unschedule until the corresponding RESP_FREE_SUB, sent by        *
 *          AUT_LED, is received.                                            *
 *****************************************************************************/

    myio_free_sub(led_iod0)          ; // Frees RED
    myio_free_sub(led_iod1)          ; // Frees GREEN
    myio_free_sub(led_iod2)          ; // Frees YELLOW


/*****************************************************************************
 * Step 2 : We call the "myio_close" procedure that calls "drv_close_device",*
 * ------   "drv_free", "drv_end" and "del_uroute".                          *
 *****************************************************************************/

    myio_close(led_iod)              ; // Closes LED device and driver
  }


/*  Procedure open_kbd --------------------------------------------------------

    Purpose : Initialize the code of the KBDITF driver and then open the
              device to get key press events from the remote control unit 
              (RCU). The opened device descriptor (Input/Output Descriptor) is
              kept in "kbd_iod".
*/

static void open_kbd(void)
  {

    int             ret              ; // Procedures returned code
    unsigned short  *pt              ; // Write pointer in "buf_rcu

/*****************************************************************************
 * Step 1 : Open the "KBDITF\DEV0" device. We call the "myio_open" simple    *
 * ------   API procedure. In fact the subroutine calls "drv_init_driver",   *
 *          "add_uroute", "drv_alloc_device", and then the "drvopen_device". *
 *          The "KBDITF\DEV0" driver is a software driver that does not      *
 *          handles any hardware by itself. With this sample application,    *
 *          the underlying hardware is an infra-red receiver. This hardware  *
 *          is managed by the "drv_rcu" driver, that in our case sends       *
 *          events to the KBDITF module, that sends DRV_KEY_PRESS and        *
 *          DRV_KEY_RELEASE events to us.                                    *
 *****************************************************************************/

    myio_open(DRVKBD,DEV0,"",&kbd_iod)  ; // Opens KBDITF\DEV0


/*****************************************************************************
 * Step 2 : Query for the RCU id for the UHF transmitter and store it in the *
 * -------  "rcusnd_id" global variable. Also query for the RCU id for the   *
 *          RCU receiver and keep it in the "rcurcv_id" global variable.     *
 *****************************************************************************/

    get_rcu_id(1          ,              // 0:Receiver 1:Transmitter
               0          ,              // numdev = the first one
               &rcusnd_id ,              // Corresponding id
               &ret        )           ; // Error code

    get_rcu_id(0          ,              // 0:Receiver 1:Transmitter
               0          ,              // numdev = the first one
               &rcurcv_id ,              // Corresponding id
               &ret        )           ; // Error code


/*****************************************************************************
 * Step 3 : Allocates one memory buffer for the RCU send function. Each      *
 * ------   symbol is made of two USHORTS (ON,OFF) durations and so 4 need,  *
 *          4 bytes per symbols and 40 * 4 = 160 bytes for 40 symbols.       *
 *****************************************************************************/

    alloc_buf(myapp_memuid ,           // Memory user id
              160          ,           // Size in bytes of requested buffer
              0            ,           // Flags
              &buf_rcu     ,           // Address of allocated buffer
              &ret          )        ; // Return code

    pt = (unsigned short*) buf_rcu   ;


/*****************************************************************************
 * Step 4 : Writes 4 symbols, 4 (ON,OFF) couples of durations in usec        *
 * ------                                                                    *
 *****************************************************************************/

    *pt++ = 400                  ;
    *pt++ = 600                  ;

    *pt++ = 200                  ;
    *pt++ = 800                  ;

    *pt++ = 200                  ;
    *pt++ = 800                  ;

    *pt++ =  200                 ;
    *pt++ = 1800                 ;
  }


/*  Procedure close_kbd -------------------------------------------------------

    Purpose : This procedure closes the keyboard interface, frees the device,
              terminates the KBDITF module, deletes the route that has been 
              created for the DRV_KEY_PRESS and DRV_KEY_RELEASE events.
*/

static void close_kbd(void)
  {

    int             ret              ; // Procedures returned code

/*****************************************************************************
 * Step 1 : We call the "myio_close" procedure that calls "drv_close_device",*
 * ------   "drv_free", "drv_end" and "del_uroute". A REQ_CLOSE is sent to   *
 *          AUT_KBD and our task is unscheduled until we receive from        *
 *          the AUT_KBD FSM the RESP_CLOSE response event. Then we send a    *
 *          REQ_FREE event request and we unschedule until RESP_FREE is      *
 *          received from AUT_KBD. And we send a REQ_END and unschedule,     *
 *          waiting for the RESP_EVENT response event.                       *
 *****************************************************************************/

    myio_close(kbd_iod)              ; // Closes KBDITF\DEV0

/*****************************************************************************
 * Step 2 : Free the symbol buffer for the RCU transmit function             *
 * -------                                                                   *
 *****************************************************************************/

    free_buf(buf_rcu   , &ret )      ; // Free "buf_rcu"

    buf_rcu = HNULL                  ; // Clear the buffer reference
  }


/*  Procedure open_dac --------------------------------------------------------

    Purpose : DAC module initialization, allocation and opening of DAC device
*/

static void open_dac(void)
  {
    int             ret              ; // Procedures returned code
    int             i                ; // Loop counter

/*****************************************************************************
 * Step 1 : We have to initialize the driver module code. To do this, we     *
 * ------   have to send a REQ_INIT request event to its VMIO automaton, in  *
 *          our case the VMIO AUT_DAC automaton. To do this, we call the     *
 *          "drv_init_driver" procedure. The AUT_DAC automaton responds with *
 *          a RESP_INIT event, we here unschedule until the reception of     *
 *          this response event. Then we have to allocate the DAC device,    *
 *          we have to send a REQ_ALLOC event to AUT_DAC and to unschedule   *
 *          until the RESP_ALLOC response event is received. This is done by *
 *          the "drv_alloc_device" procedure. Then we have to open the       *
 *          device with a REQ_OPEN request event to AUT_DAC and unschedule   *
 *          until the RESP_OPEN is received from AUT_DAC. All this is done   *
 *          with a single call to "myio_open" that issues DRV API calls. To  *
 *          be short, "myio_open" calls:                                     *
 *          - "drv_init_driver" (send REQ_INIT and wait RESP_INIT)           *
 *          - "drv_alloc_device" (send REQ_ALLOC and wait RESP_ALLOC)        *
 *          - "drv_open_device" (send REQ_OPEN and wait RESP_OPEN)           *
 *****************************************************************************/

    myio_open(DRVDAC,DEV0,dac_taglist,&dac_iod)  ; // Open DAC\DEV0


/*****************************************************************************
 * Step 2 : Allocate a memory buffer to hold samples to convert.             *
 * ------   Sample freq at 8 kHz. 80 evt/s => 100 samples / req              *
 *****************************************************************************/

    alloc_buf(myapp_memuid ,           // Memory user id
              200          ,           // Size in bytes of requested buffer
              0            ,           // Flags
              &buf_aud     ,           // Address of allocated buffer
              &ret          )        ; // Return code


/*****************************************************************************
 * Step 3 : Fill buffer with samples. We build a 400 Hz triangle wave.       *
 * ------   ~400 Hz at 8kHz => 20 samples per cycles, 10 up, 10 down         *
 *****************************************************************************/

    for (i = 0; i LT 20; i += 2)       /* Upward part of the triangle wave.  */
    {
      buf_aud[i]   = (i*0xFF) / 20   ; /*                                    */
      buf_aud[i+1] = (i*0xFF) / 40   ; /*                                    */
    }

    for (i = 0; i LT 20; i += 2)       /* Downward part of the triangle wave.*/
    {
      buf_aud[20 + i] =
                    0xFF - buf_aud[i]; /*                                    */
      buf_aud[20 + i + 1] =
                 0x7F + (i*0xFF) / 40; /*                                    */
    }

    for (i = 1 ; i LT 5 ; i++ )        /* We copy 4 times the first cycle    */
      Memcpy(buf_aud+40*i ,            /* made of 40 samples to fill the     */
             buf_aud      ,            /* 160 remaining samples of the buffer*/
             40             )        ; /*                                    */
  }


/*  Procedure close_dac -------------------------------------------------------

    Purpose : This procedure closes the DAC device, then frees this device,
              termminates the DAC driver module and finally frees the
              "buf_aud" sample buffer that has been allocated by "open_dac".
*/

static void close_dac(void)
  {

    int             ret              ; // Return code

/*****************************************************************************
 * Step 1 : We call the "myio_close" procedure that calls "drv_close_device",*
 * ------  "drv_free" and "drv_end". A REQ_CLOSE is sent to AUT_DAC and      *
 *          our task is unscheduled until we receive from the AUT_DAC FSM    *
 *          the RESP_CLOSE response event. Then we send a REQ_FREE event     *
 *          request and we unschedule until RESP_FREE is received from the   *
 *          AUT_DAC FSM. And we send a REQ_END and unschedule, waiting for   *
 *          the RESP_EVENT response event.                                   *
 *****************************************************************************/

    myio_close(dac_iod)              ; // Closes DAC\DEV0


/*****************************************************************************
 * Step 2 : We free the sample buffer that was allocated by "open_dac".      *
 * ------   The buffer address is kept in the "buf_aud" global variable      *
 *****************************************************************************/

    free_buf(buf_aud , &ret )        ; // Free "buf_aud"

    buf_aud = HNULL                  ; // Clear the reference
  }


/*  Procedure empt ------------------------------------------------------------

    Purpose : UI treatment - Do nothing, but do it well
*/
static int empt(int n)
  {
    return 0                         ; // Nothing, with no error
  }


/*  Procedure pmen ------------------------------------------------------------

    Purpose : UI treatment - Prints (sends) on the ASY0 serial line one 
              application menu, one sequence of ASCII characters.
*/

static int pmen(int n)
  {
    const char      *str             ; // Menu string address
    unsigned char   *snd             ; // Address of buffer to be sent
    int             lg               ; // Number of bytes to be sent
    const char      *ps              ; // Patch address
    char            *pd              ; // Pointer in "buf_snd[0]
    int             i                ; // Loop counter

/*****************************************************************************
 * Step 1 : Copy in the transmit buffer the characters that have to be sent  *
 * ------   in order to print the menu. They are simply stored into the      *
 *          static array "menu[]" depending on the current page.             *
 *****************************************************************************/

    str = *menu[n]                   ; // String address of the menu text
    snd = buf_snd[0]                 ; // Buffer to be sent

    lg  = strlen(str)                ; // Number of characters to be copied
                                       // and then to be sent on ASY0
    Memcpy(snd,str,lg)               ; // Copy into "buf_snd[0]" the static
                                       // part of the menu

/*****************************************************************************
 * Step 2 : If there is a title or another item to be replaced, search for   *
 * ------   the first "#" and patch at that point:                           *
 *          - If the patch address "patch[n]" is HNULL, nothing to do, go to *
 *            the next step.                                                 *
 *          - Search in "buf_snd[0]" for the first '#' character. We use     *
 *            "pd" as a current character pointer and "i" as a counter.      *
 *          - If we found a '#' before the end of the "lg" characters, we    *
 *            copy "patch" to location "pt". We take care not to copy the    *
 *            nul byte that terminates the "patch" string.                   *
 *****************************************************************************/

    ps = patch[n]                   ; // The patch address
    if (ps EQ HNULL) goto step3     ; // If we do not have a patch, continue

    for(i = 0, pd = (char*)snd ;      // Scan the characters until we find a
        i < lg && *pd != '#'   ;      // '#' or we reach the buffer end, so
        i++, pd++                )  ; // "lg" characters

    if (i < lg )                      // If we found a '#', copy the "patch"
      Strncpy(pd        ,             // at location "pd" in the buffer
              ps        ,             // We do not copy the nul terminator
              Strlen(ps)  )         ; // of "patch"


/*****************************************************************************
 * Step 3 : Send using serial DEV0, so IOD "asy_iod0", the "lg" characters   *
 * ------   that are in the buffer pointed by "buf_snd[0]". We call the      *
 *          "myio_write" subroutine that sends the buffer and unschedules us *
 *          until the effective transmission of the last byte.               *
 *****************************************************************************/

    step3                            : // Send the buffer
     
    myio_write (asy_iod0 ,             // I/O descriptor for ASY\DEV0
                snd      ,             // Address of buffer
                lg         )         ; // Number of bytes to be sent


/*****************************************************************************
 * Step 4 : Absolutely all the menus are displaying time. We could wait for  *
 * ------   the next time-out event but we may have the XX:XX:XX displayed   *
 *          up to one second. So we do not wait for the time-out and we call *
 *          the "ptim" procedure that just redraws the time.                 *
 *****************************************************************************/

    ptim(0)                          ; // Read time, convert in a string and
                                       // send it on the serial line


/*****************************************************************************
 * Step 5 : Update the "page" global variable that is the number of the page *
 * ------   that is currently displayed. This number is needed at step 5 of  *
 *          "loop_app_tsk", it is the state variable of the menu automaton.  *
 *          We also reset the count of characters of the "bufchr" input      *
 *          buffer to 0.                                                     *
 *****************************************************************************/

    page = n                         ; // Note the currently displayed menu
                                       // This is the UI state variable
    lgchr = 0                        ; // Force to empty the "bufchr" buffer

    return 0                         ; // No error
  }


/*  Procedure pdtc ------------------------------------------------------------

    Purpose : Display the touch coordinates.
*/

static int pdtc(void)
  {

    unsigned char   *snd             ; // Address of buffer to be sent
    char            *pt              ; // Write pointer

/*****************************************************************************
 * Step 1 : Put in the transmit buffer the characters that have to be sent   *
 * ------   in order to print the touch coordinates. We:                     *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will save current ANSI terminal pointer position. Then we      *
 *            increment the "number of bytes".                               *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will move the console cursor the location where the first      *
 *            character of the touch coordinates will be printed. Then we    *
 *            increment the "number of bytes".                               *
 *          - Write into the config buffer the touch coordinates. Then we    *
 *            increment the "number of bytes".                               *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will restore terminal pointer position. Then we increment the  *
 *            "number of bytes".                                             *
 *          The cursor "normal" position is on the right of the last line    *
 *          of the current menu page "Enter command". But the line number    *
 *          where that prompt lays is not the same one for all the menu      *
 *          pages. So whatever the current menu page is, we request the      *
 *          terminal to save the cursor position and to restore it after     *
 *          current time has been displayed.                                 *
 *****************************************************************************/

    snd = buf_snd[0]                 ; // Buffer to be sent address
    pt  = (char*) snd                ; // Init write pointer = beg of buffer

    strcpy(pt, loc_save)             ; // Copy into the transmit buffer
                                       // the ANSI escape sequence
    pt += sizeof(loc_save) - 1       ; // Increment the number of bytes

    strcpy(pt, loc_t_coord)          ; // Move cursor to line = 16 column = 53
    pt += sizeof(loc_t_coord) - 1    ; // Increment the number of bytes

    memcpy(pt, i2c_rx, 3)            ; // Copy touch coordinates
    pt += 3                          ; // Increment the number of bytes

    strcpy(pt, loc_restore)          ; // Copy into the transmit buffer
                                       // the ANSI escape sequence
    pt += sizeof(loc_restore) - 1    ; // Increment the number of bytes

/*****************************************************************************
 * Step 2 : Send using serial DEV0, so IOD "asy_iod0". We call the           *
 * ------   "myio_write" procedure that sends a REQ_WRITE request event to   *
 *          the AUT_ASY VMIO automaton. Then we unschedule until the         *
 *          RESP_WRITE response event is received.                           *
 *****************************************************************************/

    myio_write (asy_iod0       ,       // I/O descriptor for ASY\DEV0
                snd            ,       // Address of buffer
                pt - (char*)snd)     ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure ptim ------------------------------------------------------------

    Purpose : UI treatment - Read time, convert it in a ASCII string and then 
              prints (sends) it on the ASY0 serial line.
*/

static int ptim(int n)
  {
    unsigned char   *snd             ; // Address of buffer to be sent
    int             d, m, y          ; // Day, Month, Year
    int             h, mi, s, ms     ; // Hour, minutes, seconds, milliseconds
    char            *pt              ; // Write pointer
    int             lg               ; // Number of bytes to be sent

/*****************************************************************************
 * Step 1 : We first retrieve date and time since boot. We call the          *
 * ------   "tim_get" procedure to obtain date and time.                     *
 *****************************************************************************/

    tim_get(&d  ,                      // day
            &m  ,                      // Month
            &y  ,                      // Year
            &h  ,                      // Hour (0..23)
            &mi ,                      // Minutes (0..59)
            &s  ,                      // Seconds (0..59)
            &ms   )                  ; // Milliseconds (0..999)


/*****************************************************************************
 * Step 2 : Put in the transmit buffer the characters that have to be sent   *
 * ------   in order to print elapsed time since boot. We are using "lg" as  *
 *          the current number of bytes inside the transmit buffer. We:      *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will save current ANSI terminal pointer position. Then we      *
 *            increment the "lg" number of bytes.                            *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will move the console cursor the location where the first      *
 *            character of the date will be printed. Then we increment the   *
 *            "lg" number of bytes.                                          *
 *          - Write into the config buffer the "HH:MM:SS" string representing*
 *            the elapsed time. Then we increment the "lg" number of bytes.  *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will restore terminal pointer position. Then we increment the  *
 *            "lg" number of bytes.                                          *
 *          The cursor "normal" position is on the right of the last line    *
 *          of the current menu page "Enter command". But the line number    *
 *          where that prompt lays is not the same one for all the menu      *
 *          pages. So whatever the current menu page is, we request the      *
 *          terminal to save the cursor position and to restore it after     *
 *          current time has been displayed.                                 *
 *****************************************************************************/

    snd = buf_snd[0]                 ; // Buffer to be sent address
    pt  = (char*) snd                ; // Init write pointer = beg of buffer
    lg  = 0                          ; // Init number of writtent bytes

    strcpy(pt, loc_save)             ; // Copy into the transmit buffer
                                       // the ANSI escape sequence
    pt += sizeof(loc_save) - 1       ; // Increment the number of bytes
    lg += sizeof(loc_save) - 1       ; // Increment the number of bytes

    strcpy(pt, loc_time)             ; // Move cursor to line = 3 column = 55
    pt += sizeof(loc_time) - 1       ; // Increment the number of bytes
    lg += sizeof(loc_time) - 1       ; // Increment the number of bytes

    hsprintf(pt               ,        // Put in the transmit buffer
             "%02d:%02d:%02d" ,        // 8 characters HH:MM:SS
             h, mi, s              ) ; //
    pt += 8                          ; // Update the write pointer
    lg += 8                          ; // Update the number of bytes

    strcpy(pt, loc_restore)          ; // Copy into the transmit buffer
                                       // the ANSI escape sequence
    pt += sizeof(loc_restore) - 1    ; // Increment the number of bytes
    lg += sizeof(loc_restore) - 1    ; // Increment the number of bytes


/*****************************************************************************
 * Step 2 : Send using serial DEV0, so IOD "asy_iod0", the "lg" characters   *
 * ------   that are in the buffer pointed by "buf_snd[0]". We call the      *
 *          "myio_write" procedure that sends a REQ_WRITE request event to   *
 *          the AUT_ASY VMIO automaton. Then we unschedule until the         *
 *          RESP_WRITE response event is received.                           *
 *****************************************************************************/

    myio_write (asy_iod0  ,            // I/O descriptor for ASY\DEV0
                snd       ,            // Address of buffer
                lg         )         ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure pgrp ------------------------------------------------------------

    Purpose : UI treatment - Prints (sends) on the serial line the group of 
              string statuses. The input parameter "n" value is one the
              GMAIN, GASY1, GASY2, GGPIO, GBUT or VINPUT defines.
*/

static int pgrp(int n)
  {
    unsigned char   *snd             ; // Address of buffer to be sent
    int             i, ind           ; // Loop counter 
    int             sz               ; // Length of string item
    char            *pt              ; // Write pointer
    int             lg               ; // Number of bytes to be sent
    const char      *const *tab      ; // List of string addresses
    const char      *str             ; // String address
    char            buf[20]          ; // Decimal integer string
    int             val              ; // Copy of "cfg[ind]"
    int             cnt              ; // Count of "cfg[]" values
    int             col              ; // Horizontal tabulation
    int             len              ; // Item width in characters

/*   GMAIN   GCONF    GASY1   GASY2   GGPIO   GBUT   GRCU
     -----   -------  -----   -----   -----   -----  -----  */
static const int tfirst[7] =
{    VRED,   VASY1PT, VBAUD1, VBAUD2, VPTIM , VMON1, VINPUT };
static const int tcnt  [7] = 
{    7   ,   3      , 9     , 9     , 2     , 9    , 6      };
static const int tcol  [7] = 
{    0   ,   0      , 0     , 0     , 0     , 0    , 1      };
static const int tlen  [7] = 
{    7   ,   7      , 7     , 7     , 7     , 7    , 16     };

/*****************************************************************************
 * Step 1 : Initialization of local variables:                               *
 * ------   - We will use "ind" as an index in the "cfg[]" table of values.  *
 *            The "tfirst[]" const table gives us the index of the first     *
 *            "cfg[]" integer to be displayed. For the MAIN menu we have     *
 *            "n = 0" and "ind" is set to VRED. With the MASY1 menu we have  *
 *            "n = 1" and "ind" is set to VBAUD. And with the MGPIO menu we  *
 *            have "n = 2" and "ind" is set to VPTIM.                        *
 *          - We set "cnt" to the number of "cfg[]" values to be displayed.  *
 *            We set "cnt" to "tcnt[n]" constant table value.                *
 *          - We use "pt" as a write pointer in the "buf_snd" transmit       *
 *            buffer. We initialize "pt" to the "buf_snd[0]" start address.  *
 *          - We use "lg" as a count of written bytes in "buf_snd", we       *
 *            initialize to 0 this count.                                    *
 *****************************************************************************/

    ind   = tfirst[n]                ; // Start index in the "cfg[]" array
    cnt   = tcnt[n]                  ; // Count of "cfg[]" values to be used
    col   = tcol[n]                  ; // Horizontal tabulation
    len   = tlen[n]                  ; // Item length
    snd   = buf_snd[0]               ; // Buffer to be sent address
    pt    = (char*) snd              ; // Init write pointer = beg of buffer
    lg    = 0                        ; // Init number of written bytes


/*****************************************************************************
 * Step 2 : Put in the transmit buffer the characters that have to be sent   *
 * ------   in order to print the "cnt" menu statuses. We are using "pt" as  *
 *          a current write position in the transmit buffer and "lg" is the  *
 *          current number of bytes inside the transmit buffer. We start by  *
 *          saving the current terminal pointer position (input):            *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will save current pointer position. Then we increment the "pt" *
 *            current write address and the "lg" number of bytes.            *
 *          Then we loop on the "cnt" values. For each value:                *
 *          - Copy into the transmit buffer the ASCII escape sequence that   *
 *            will move the console cursor to the location where the first   *
 *            status character will be drawn. We have a static array of      *
 *            address strings for that (loc_cfg[9]). Then we increment the   *
 *            "pt" current write address and the "lg" number of bytes.       *
 *          - Copy in the transmit buffer the string that is associated to   *
 *            "cfg[ind]". We have a "str_cfg[]" list of string list that     *
 *            gives addresses of arrays of string addresses. So the value    *
 *            of "str_cfg[ind]" is the address of the array of string        *
 *            addresses for variable "ind". Then the "cfg[ind]" value is     *
 *            used as an index in that table and so " str_cfg[ind][cfg[ind]]"*
 *            is the address of the string to be displayed.                  *
 *          - All the strings do not have exactly the same number of         *
 *            characters. So we increment the "pt" write pointer and the     *
 *            "lg" count of written bytes by the item size string.           *
 *          Then we restore the terminal pointer state:                      *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will restore pointer position. Then we increment the "pt"      *
 *            current write address and the "lg" number of bytes.            *
 *****************************************************************************/

    strcpy(pt, loc_save)             ; // Copy into the transmit buffer
                                       // the ANSI escape sequence
    pt += sizeof(loc_save) - 1       ; // Increment the write pointer
    lg += sizeof(loc_save) - 1       ; // Increment the number of bytes

    for (i = 0 ; i < cnt ; i++, ind++) // Loop on the "cnt" menu statuses
      {                                // 
        str = loc_cfg[i][col]        ; // String escape sequence address
        sz  = strlen(str)            ; // Copy into the transmit buffer 
        strcpy(pt,str)               ; // the ANSI escape sequence
        pt += sz                     ; // Increment the write pointer
        lg += sz                     ; // Increment the number of bytes

        tab = str_cfg[ind]           ; // List of string addresses
        val = cfg    [ind]           ; // "val" is the index in "tab"

        if (tab NE HNULL)              // If we do have a list of strings
          str = tab[val]             ; // Select string "val" from the list
        else                           // Otherwise, we convert
          BEGIN                        // the "cfg[ind]" value
            hsprintf(buf,"%d",val)   ; // in an integer decimal string
            str = buf                ; // and "str" points to this string
          END_IF                       //

        memcpy(pt,                     // Fill "len" characters with "-"
               "-----------------",    // wich makes up the
               len                  ); // background
        strcpy(pt,str)               ; // The displayed item string
        pt += len                    ; // Increment the write pointer
        lg += len                    ; // Increment the number of bytes
      }

    strcpy(pt, loc_restore)          ; // Copy into the transmit buffer
                                       // the ANSI escape sequence
    pt += sizeof(loc_restore) - 1    ; // Increment the write pointer
    lg += sizeof(loc_restore) - 1    ; // Increment the number of bytes


/*****************************************************************************
 * Step 3 : Send using serial DEV0, so IOD "asy_iod0", the "lg" characters   *
 * ------   that are in the buffer pointed by "buf_snd[0]". We call the      *
 *          "myio_write" procedure that sends a REQ_WRITE request event to   *
 *          the AUT_ASY VMIO automaton. Then we unschedule until the         *
 *          RESP_WRITE response event is received.                           *
 *****************************************************************************/
    
    myio_write (asy_iod0 ,            // I/O descriptor for ASY\DEV0
                snd      ,            // Address of buffer
                lg        )         ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure pcmd ------------------------------------------------------------

    Purpose : UI treatment - Prints (sends) on the serial line the last command
              received, so one "c" character.
*/

static int pcmd(int c)
  {
    unsigned char   *snd             ; // Address of buffer to be sent
    const char      *str             ; // Address of the ANSI escape sequence
    int             lg               ; // Number of bytes to be sent

/*****************************************************************************
 * Step 1 : Put in the transmit buffer the characters that have to be sent   *
 * ------   in order to echo the 'c' command character. We are using "lg"    *
 *          as the current number of bytes inside the transmit buffer. We:   *
 *          - Copy into the transmit buffer the ANSI escape sequence that    *
 *            will move pointer to the right place. This place depends on    *
 *            the current page. We have a static array of address strings    *
 *            for that purpose: loc_cmd[].                                   *
 *          - Copy in the transmit buffer the character to be echoed.        *
 *****************************************************************************/

    snd = buf_snd[0]                 ; // Address of buffer to be sent
    str = loc_cmd[page]              ; // Address of the ANSI escape sequence
    lg  = strlen( str )              ; // Number of bytes in the buffer

    strcpy((char*)snd, str)          ; // ANSI Escape Sequence
                                       // Move cursor to line, column

    snd[lg] = (char)c                ; // Put the echo in transmit buffer

    lg += 1                          ; // Update number of bytes in the buffer


/*****************************************************************************
 * Step 2 : Send using serial DEV0, so IOD "asy_iod0", the "lg" characters   *
 * ------   that are in the buffer pointed by "buf_snd[0]". We call the      *
 *          "myio_write" procedure that sends a REQ_WRITE request event to   *
 *          the AUT_ASY VMIO automaton. Then we unschedule until the         *
 *          RESP_WRITE response event is received.                           *
 *****************************************************************************/
    
    myio_write (asy_iod0  ,            // I/O descriptor for ASY\DEV0
                snd       ,            // Address of buffer
                lg         )         ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure pchr ------------------------------------------------------------

    Purpose : UI treatment - Prints (sends) on the serial line one ASCII
              character (ASCII code is c).
*/

static int pchr(int c)
  {
    unsigned char   *snd             ; // Address of buffer to be sent
    int             lg               ; // Number of bytes to be sent


/*****************************************************************************
 * Step 1 : Send using serial DEV0, so IOD "asy_iod0", the "c" character.    *
 * ------   We copy it in "buf_snd[0] then we call the "myio_write"          *
 *          procedure that sends a REQ_WRITE request event to the AUT_ASY    *
 *          VMIO automaton. Then we unschedule until the RESP_WRITE response *
 *          event is received.                                               *
 *          ---------------------------------------------------------------- *
 *          With a BACKSPACE ('c' is 8) we have a special treatment:         *
 *          - We send a BACKSPACE,                                           *
 *          - We send a SPACE    ,                                           *
 *          - We send a BACKSPACE,                                           *
 *****************************************************************************/
    
    snd    = buf_snd[0]              ; // Address of buffer to be sent

    if (c EQ 8)                        // If we have a BACKSPACE to echo
      {                                //
        if (lgchr == 0) return 0     ; // Do nothing if buffer is empty
        snd[0] = snd[2] = 8          ; // ASCII code for BACKSPACE
        snd[1] =          32         ; // ASCII code for SPACE
        lg     =          3          ; // We send 3 bytes
      }      
    else                               // If we have a "normal" ASCII code
      {                                //
        if (lgchr >= LGCHR) return 0 ; // Do noting if buffer is full
        snd[0] = c                   ; // Copy the charcater to be sent
        lg     = 1                   ; // We send one byte
      }         

    myio_write (asy_iod0  ,            // I/O descriptor for ASY\DEV0
                snd       ,            // Address of buffer
                lg         )         ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure pcap ------------------------------------------------------------

    Purpose : UI treatment - Prints the RCU symbols captured values
*/

static int pcap(int n)
  {
    unsigned char   *snd             ; // Address of buffer to be sent
    int             sz               ; // Length of string item
    char            *pt              ; // Write pointer
    int             lg               ; // Number of bytes to be sent
    unsigned short  *ad              ; // Address of measures
    int             nb               ; // Number of measures
    int             y, i , k         ; // Loop counters      

/*****************************************************************************
 * Step 1 : Initialization of local variables:                               *
 * ------   - We use "pt" as a write pointer in the "buf_snd" transmit       *
 *            buffer. We initialize "pt" to the "buf_snd[0]" start address.  *
 *          - We use "lg" as a count of written bytes in "buf_snd", we       *
 *            initialize to 0 this count.                                    *
 *          - We retreive from the "adresse" field of the incoming           *
 *            DRV_KEY_PRESS event the start address of the measure's table.  *
 *            As we are a task, we have in the global variable "task_evt" a  *
 *            copy of the just received event.                               *
 *          - We retreive from the "longueur" field of the incoming event    *
 *            the count of measures (2 x symbol count).                      *
 *****************************************************************************/

    snd = buf_snd[0]                 ; // Buffer to be sent address
    pt  = (char*) snd                ; // Init write pointer = beg of buffer
    lg  = 0                          ; // Init number of written bytes
    ad  = (USHORT*) task_evt.adresse ; // Measure's table start address
    nb  = task_evt.longueur          ; // Count of "unsigned short's"


/*****************************************************************************
 * Step 2 : Fill the transmit buffer "buf_snd[0]". We have 15 lines we can   *
 * ------   use and we can display 10 USHORTS on each line, so a maximum     *
 *          of 15 x 10 = 150 decimal number of 5 characters each may be      *
 *          drawn. We have two imbricated loops:                             *
 *          - First loop in on lines, from 5 to 19 (so 15 lines). We store   *
 *            the ANSI escape sequence that moves the cursor on line "y",    *
 *            column 5.                                                      *
 *          - Second loop is on 10 USHORT's. At each step we put 6 ASCII     *
 *            characters, a right justified 5 character decimal number       *
 *            followed by a "space" character.                               *
 *          We stop the loop when the "nb" numbers have been processed. Then *
 *          we add the ANSI escape sequence that moves the cursor on line 22 *
 *          column 43.                                                       *
 *****************************************************************************/

    for(y = 5, k = 0; y LE 19; y++)    /* We loop on lines 5 to 19 of the    */
      BEGIN                            /* terminal. We store the ANSI escape */
        hsprintf(pt,"\x1B[%d;5H",y)  ; /* sequence that moves the terminal   */
        sz = strlen(pt)              ; /* cursor on line "y" column 5. Then  */
        lg += sz                     ; /* increment the "lg" count of        */
        pt += sz                     ; /* characters and the "pt" write      */
                                       /* address in "buf_snd[0]"            */
                                       /*                                    */
        for(i = 0; i LT 10; i++, k++)  /* Loop on the 10 USHORT's, so 5      */
          BEGIN                        /* (ON time, SYMB time) that we want  */
                                       /* to display on the current line "y" */
            if (k GE nb) goto end    ; /* If all the USHORT's have been      */
                                       /* treated, we exit from this loop    */
            hsprintf(pt,"%5d ",ad[k]); /* Store 5 decimal digits and one     */
            lg += 6                  ; /* "space" at address "pt", then      */
            pt += 6                  ; /* increment by 6 the number of       */
          END_FOR                      /* written bytes and also the current */
                                       /* "pt" write address                 */
      END_FOR                          /*                                    */
                                       /*                                    */
    end                              : /* We add the ANSI escape sequence    */
    strcpy(pt,"\x1B[22;43H")         ; /* that moves the terminal cursor     */
    lg += 8                          ; /* on terminal line 22, column 43     */


/*****************************************************************************
 * Step 3 : Send using serial DEV0, so IOD "asy_iod0", the "lg" characters   *
 * ------   that are in the buffer pointed by "buf_snd[0]". We call the      *
 *          "myio_write" procedure that sends a REQ_WRITE request event to   *
 *          the AUT_ASY VMIO automaton. Then we unschedule until the         *
 *          RESP_WRITE response event is received.                           *
 *****************************************************************************/

    myio_write (asy_iod0 ,             // I/O descriptor for ASY\DEV0
                snd      ,             // Address of buffer
                lg        )          ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure perr ------------------------------------------------------------

    Purpose : UI treatment - Prints (sends) on the serial line the decimal
              value or "errcode".
*/
static int perr(int c)
  {
    unsigned char   *snd             ; // Address of buffer to be sent
    char            *pt              ; // Write pointer in "snd"
    int             lg               ; // Number of bytes to be sent

    snd = buf_snd[0]                 ; // Address of buffer to be sent
    pt  = (char*) snd                ; // Write pointer in the buffer

    strcpy(pt, loc_error)            ; // ANSI Escape Sequence
                                       // Move cursor to line, column
    lg  = strlen(pt)                 ; // Count of written bytes
    pt += lg                         ; // Update the write pointer

    hsprintf(pt,"Error code %d",       // Add the error message in 
             errcode            )    ; // the buffer

    lg += strlen(pt)                 ; // Update the count of written bytes

    myio_write (asy_iod0  ,            // I/O descriptor for ASY\DEV0
                snd       ,            // Address of buffer
                lg         )         ; // Number of bytes to be sent

    return 0                         ; // No error
  }


/*  Procedure tcfg ------------------------------------------------------------

    Purpose : UI treatment - Toggles one "cfg[]" value (0->, 1->0)
*/

static int tcfg(int n)
  {
    cfg[n] ^= 1                      ; // Toggles "cfg[n]" value

    return 0                         ; // No error
  }


/*  Procedure tinp ------------------------------------------------------------

    Purpose : UI treatment - Toggles VINPUT and VNUMRCU
*/

static int tinp(int n)
  {
    if (n EQ VINPUT)
      {
        cfg[VINPUT] ^= 1             ; // 0->1, 1->0
        cfg[VNUMRCU] = vnumrcu[cfg[VINPUT]] ; // 0:0, 1:3
      }
    else
      {
        cfg[VNUMRCU] ++              ; // 0->1, 1->0, 2->3, 3->4, 4->5, 5->6

        if (cfg[VNUMRCU] EQ NBRCU_IR) 
          cfg[VNUMRCU] = 0           ;

        if ( cfg[VNUMRCU] EQ 
             (NBRCU_IR+NBRCU_UHF) ) 
          cfg[VNUMRCU] = NBRCU_IR    ;
      }

    return 0                         ; // No error
  }


/*  Procedure tssy ------------------------------------------------------------

    Purpose : UI treatment - Toggles VONMIN/VONMAX/VSTMIN/VSTMAX
*/

static int tssy(int n)
  {
    int             swpos            ; // Current switch position
    int             numrcu           ; // RCU order number in database
    char            name[20]         ; // Current RCU name
    int             otnom            ; // ON time nominal in usec
    int             stnom            ; // SYMB time nominal in usec
    int             ret              ; // Error code

/*****************************************************************************
 * Step 1 : Retreive the RCU database entry number "numrcu" for the RCU that *
 * ------   is currently associated with receiver "rcurcv_id". The "numrcu"  *
 *          value and the "swpos" switch positions are modified when the     *
 *          "change_rcu" procedure is called.                                *
 *****************************************************************************/

    get_rcu    (rcurcv_id    ,         // The receiver device identifier
                &swpos       ,         // The current switch position
                &numrcu      ,         // The RCU database order number
                name         ,         // The corresponding RCU name
                &ret          )      ; // Error code

/*****************************************************************************
 * Step 2 : Retreive the timings in useconds for the first symbol (0) for    *
 * ------   that RCU. By convention, the first received message symbol is    *
 *          always available from entry 0 of the RCU symbol list.            *
 *****************************************************************************/

    get_rcu_sym(numrcu             ,   // The RCU database order number
                0                  ,   // The RCU symbol order number
                (int*)&cfg[VONMIN] ,   // ON   time  minimal in usec
                (int*)&cfg[VONMAX] ,   // ON   time  maximal in usec
                (int*)&cfg[VSTMIN] ,   // SYMB time  minimal in usec
                (int*)&cfg[VSTMAX] ,   // SYMB time  maximal in usec
                &otnom             ,   // ON   time  nominal in usec
                &stnom             ,   // SYMB time  nominal in usec
                &ret                ); // Error code

    return 0                         ; // No error
  }


/*  Procedure tchr ------------------------------------------------------------

    Purpose : UI treatment - Add a new character to the input line
*/

static int tchr(int n)
  {
    if (n == 8)                        // If we have a BACKSPACE
      {
        if ( lgchr <= 0) return 0    ;
        lgchr --                     ;
      }
    else                               // If we have a "normal" ASCII code
      {
        if ( lgchr >= LGCHR) return 0;
        bufchr[lgchr++] = n          ;
      }

    return 0                         ; // No error
  }


/*  Procedure scfg ------------------------------------------------------------

    Purpose : UI treatment - Sets one "cfg[]" value to "opt_val -1".
*/

static int scfg(int n)
  {
/*****************************************************************************
 * Step 1 : The last user choice is an integer number ranging from 1 to 9    *
 * ------   and its value is available from the "opt_val" global variable.   *
 *          We store that choice in the indicated "cfg[n]" global array. We  *
 *          substract 1 because the value will be used as C table indexes,   *
 *          that starts with 0. The tables that will take "cfg[n]" as        *
 *          indexes are "str_cfg[]" and "str_tag[]".                         *
 *****************************************************************************/

    cfg[n] = opt_val - 1             ; // Set "cfg[n]" to the user choice - 1

    return 0                         ; // No error
  }


/*  Procedure sval ------------------------------------------------------------

    Purpose : UI treatment - Calls "myio_setval", using "cfg[n]" as a taglist
              selector.
*/

static int sval(int n)
  {
    const char *const      *tag    ; // Address of the ANSI escape sequence
    int                    ret     ; // Error code

/*****************************************************************************
 * Step 1 : We are going to call "myio_setval" with a const string as second *
 * ------   parameter. For each variable "n" ("n" is an index in "cfg[]")    *
 *          we have a list of strings, that is in fact an array of string    *
 *          addresses. For example, for N = VBAUD, the corresponding list of *
 *          strings is "tag_baud". We have an array "str_tag[]" that         *
 *          contains all the addresses of the string list, so the            *
 *          "str_tag[VBAUD]" value is the address of "str_tag[VBAUD]". Here  *
 *          we set the "tag" local variable to "str_tag[n]", so to           *
 *          "tag_baud" if "n" value is VBAUD.                                *
 *****************************************************************************/

    tag = str_tag[n]               ; // Address of array of C string addresses


/*****************************************************************************
 * Step 2 : Now we call the "myio_setval" procedure, that calls 'drv_setval" *
 * -------  - The first parameter is the device IOD or the sub-device IOD.   *
 *            At step 3 of "loop_app_tsk" we had stored in "iods[n]" the IOD *
 *            value to be used here. So "iods[n]" is our first parameter.    *
 *          - The second parameter is the C string of index "cfg[n]" in the  *
 *            "tag" list of strings, so "tag[ cfg[n] ]" is the address of    *
 *            the const string to be provided as a second parameter.         *
 *          We have 3 variables that do not imply any call to "drv_setval",  *
 *          VASY1 (3), VDAC (4) and VSPEED (13). Indeed the present          *
 *          procedure must not be called with each of those values. We have  *
 *          HNULL values in "str_tag[VASY1]", in "str_tag[VDAC]" and         *
 *          "str_tag[VSPEED]". In case of any error in the const tables, we  *
 *          here check that "tag" is not HNULL (if yes, it's a bug...).      *
 *****************************************************************************/

    if (tag != HNULL)                // "tag" must not be HNULL
      ret = myio_setval(iods[n],     // IOD to be used with "drv_setval"
                       tag[cfg[n]]); // C string to be used with "drv_setval"
    else
      vhalt()                      ; // Here we have a bug ...

    return ret                     ; // No error
  }


/*  Procedure sled ------------------------------------------------------------

    Purpose : UI treatment - Starts/Stops LED blinking.
*/

static int sled(int n)
  {
    int            ret               ; // Error code

/*****************************************************************************
 * Step 1 : The LED driver has a stack of blinking patterns for each LED.    *
 * ------   Before pushing a new blinking pattern in the stack, we request   *
 *          to empty the blink pattern stack to the led driver. To do this,  *
 *          we send a REQ_SETVAL "CMD=POPALL" event to the LED driver, so we *
 *          call the "myio_setval" procedure that send the REQ_SETVAL event  *
 *          to the VMIO AUT_LED automaton. Then we ask to be unscheduled     *
 *          until the response event RESP_SETVAL is received.                *
 *****************************************************************************/

    myio_setval(iods[n],               // Always call "drv_setval" with
                led_tagpop)          ; // "CMD=POPALL"


/*****************************************************************************
 * Step 2 : The blink pattern stack for our LED is now empty. We push a new  *
 * ------   blinking pattern: we call "myio_setval" that sends to the VMIO   *
 *          AUT_LED a REQ_SETVAL that carries the new blinking pattern, that *
 *          has to be ended by "CMD=PUSH". And then we ask to be unscheduled *
 *          until the RESP_SETVAL response event, replied by AUT_LED, has    *
 *          been received.                                                   *
 *          The "myio_setval" procedure needs two input parameters:          *
 *          - The first parameter is the device or sub-channel IOD value. In *
 *            our case, the "loop_app_tsk" has stored in "iods[]" the IOD    *
 *            value to be used for each "n" value. So we use here "iods[n]"  *
 *            as first "myio_setval" parameter value.                        *
 *          - The second parameter is a character string, the TAG list. In   *
 *            our case, we have const data (C strings) for all the possible  *
 *            tag lists. We have also string lists, that are just lists of   *
 *            string addresses. For example the VBAUD parameter may have 5   *
 *            possible values (CFG_9600 0, CFG_19200 1, CFG_38400 2,         *
 *            CFG_57600 3 and CFG_115200 4). So we have 5 corresponding      *
 *            taglist strings and the "tag_baud[5]" contains the 5 addresses *
 *            of those 5 strings. But also, we have one more array           *
 *            "str_tag[]" that contains all the addresses of the list        *
 *            addresses, for example "str_tag[5]" contains the address of    *
 *            "tag_baud". Therefore, "str_tag[n]" is the address of the      *
 *            string list for variable "n". Then, the value of variable "n"  *
 *            is "cfg[n]" and therefore "str_tag[n][ cfg[n] ]" is the C      *
 *            string to be used as tag list, and the second parameter to be  *
 *            given to "myio_setval".                                        *
 *****************************************************************************/

    ret = myio_setval(iods[n],         // Push the pattern that corresponds
                str_tag[n][cfg[n]])  ; // to "cfg[n] = 0" or "cfg[n] = 1"

    return ret                       ; // No error
  }


/*  Procedure sint ------------------------------------------------------------

    Purpose : UI treatment - Converts the decimal string from "bufstr" to a
              binary integer value.
*/

static int sint(int n)
  {
    if (lgchr == 0)                    // If the buffer is empty we
      bufchr[lgchr++] = '0'          ; // put a '0' string (one byte)

    bufchr[lgchr] = 0                ; // Add the nul end of C string

    hsscanf(bufchr,"%d",&cfg[n])     ; // Convert decimal string to integer

    return 0                         ; // No error
  }


/*  Procedure rasy ------------------------------------------------------------

    Purpose : UI treatment - Start or stops the continuous sending on data 
              using ASY1/ASY2. We send a SND_START or SND_STOP event according 
              to the value of "cfg[ VASY1/VASY2 ]". We use logical way 
              VLASY1/VLASY2 of the AUT_USND FSM. The "n" input parameter value
              is VASY1 or VASY2.
*/

static int rasy(int n)
  {
    int                vl            ; // AUT_USND logical way
    unsigned char     *snd           ; // Buffer to be sent

/*****************************************************************************
 * Step 1 : The value of "n" is VASY1 or VASY2. We select the "vl" logical   *
 * ------   way of AUT_USND and the iod to be used, we use VLASY1 with ASY1  *
 *          ("n" is VASY1) or VLASY2 with ASY2 (n is VASY2).                 *
 *****************************************************************************/

    vl  = (n == VASY1) ? VLASY1 :      // Logical way of AUT_UNSD is VLASY1
                         VLASY2      ; // or VLASY2

    snd = buf_snd[(n == VASY1) ? 1:2]; // Buffer is "buf_snd[1]" with ASY1
                                       // and "buf_snd[2]" with ASY2

/*****************************************************************************
 * Step 2 : According the value of "cfg[n]", send a SND_START event or a     *
 * ------   SND_STOP event to the AUT_USND FSM. Then wait (unschedule) for   *
 *          the response event of AUT_USND (R_SND_START or R_SND_STOP). We   *
 *          use "req_snd_start" or "req_snd_stop". Apart from the code event *
 *          SND_START and the logical way number "vl, the start event also   *
 *          conveys 3 parameters that are:                                   *
 *          - The IOD to be used for transmission, here "iods[n]".           *
 *          - The address of the buffer to be cyclically send, here          *
 *            "buf_snd[1].                                                   *
 *          - The count of bytes in the buffer, here 256 bytes.              *
 *****************************************************************************/

    if ( cfg[n] )                      // If new state is 1, send a
      req_snd_start(vl      ,          // SND_START event to LW 0/1 of AUT_USND
                    iods[n] ,          // Iod for the ASY1/ASY2
                    snd     ,          // Data buffer for the ASY1/ASY2
                    256      )       ; // Number of bytes in buffer
    else                               // Else, new state is 0, send a
      req_snd_stop(vl)               ; // SND_STOP event to LW 0/1 of AUT_USND

    return 0                         ; // No error
  }


/*  Procedure rdac ------------------------------------------------------------

    Purpose : UI treatment - Start or stops the continuous sending on data 
              using DAC. We send a SND_START or SND_STOP event according to
              the value of "cfg[VDAC]". We use logical way VLDAC of the
              AUT_USND FSM.
*/

static int rdac(int n)
  {
/*****************************************************************************
 * Step 1 : According the value of "cfg[VDAC]", send a SND_START event or a  *
 * ------   SND_STOP event to the AUT_USND FSM. Then wait (unschedule) for   *
 *          the response event of AUT_USND (R_SND_START or R_SND_STOP). We   *
 *          use "req_snd_start" or "req_snd_stop". Apart from the code event *
 *          SND_START and the logical way number VLDAC, the start event      *
 *          also conveys 3 parameters that are:                              *
 *          - The IOD to be used for transmission, here "dac_iod".           *
 *          - The address of the buffer to be cyclically send, here          *
 *            "buf_aud".                                                     *
 *          - The count of bytes in the buffer, here 200 bytes.              *
 *****************************************************************************/

    if ( cfg[VDAC] )                   // If new state is 1, send a
      req_snd_start(VLDAC  ,           // SND_START event to LW 2 of AUT_USND
                    dac_iod,           // Iod for the DAC
                    buf_aud,           // Data buffer for the DAC
                    200     )        ; // Number of bytes in buffer
    else                               // Else, new state is 0, send a
      req_snd_stop(VLDAC)            ; // SND_STOP event to LW 2 of AUT_USND

    return 0                         ; // No error
  }


/*  Procedure rrcu ------------------------------------------------------------

    Purpose : UI treatment - Start or stops the period sending of one RCU
              message. We send a SND_START or SND_STOP event according to
              the value of "cfg[VUHFRCU]". We use logical way VLSNDRCU of the
              AUT_USND FSM.
*/

static int rrcu(int n)
  {
    if ( cfg[n] )                      // If new state is 1, send a
      req_snd_start(VLSNDRCU   ,       // SND_START event to LW 3 of AUT_USND
                    iods[n]    ,       // Iod no for the RCU
                    buf_rcu    ,       // Data buffer for the RCU transmitter
                    0x00030004  )    ; // 4 symbols in the buffer
                                       // 3 consecutive repeats
    else                               // Else, new state is 0, send a
      req_snd_stop(VLSNDRCU)         ; // SND_STOP event to LW 3 of AUT_USND

    return 0                         ; // No error
  }


/*  Procedure ewreq -----------------------------------------------------------

    Purpose : UI treatment - Writes in EEPROM (offset specified). We call the 
              non blocking procedure i2c_send which writes in the EEPROM.
    Note    : The EEPROM AT24C08A is used.
*/

static int ewreq(int n)
  {
    unsigned char *ptr               ; // Pointer
    unsigned char i                  ; // Index

/*****************************************************************************
 * Step 1 : Fill the frame to be transmitted:                                *
 * ------   - Message length (without address I2C)                           *
 *          - Address I2C of the EEPROM                                      *
 *          - Offset in EEPROM (ex: 0x0322)                                  *
 *          - Data to write                                                  *
 *****************************************************************************/

    ptr    = i2c_tx_frame            ; // Init of the pointer
    *ptr++ = n + 2                   ; // Message length (without address I2C)
    *ptr++ = I2C_ADDR_EEP_W          ; // Address I2C of the EEPROM
    *ptr++ = 0x03                    ; // Offset in EEPROM (0x0322)
    *ptr++ = 0x22                    ; //
    for (i = 1; i <= n; i++)           // Data to write
      *ptr++ = n + i                 ; //
    *ptr   = 0x00                    ; // Next message length = 0: current 
                                       // block = last block to be transmitted

/*****************************************************************************
 * Step 2 : Deposit an event to the automaton I2C. This event contains the   *
 * ------   frame to be transmitted.                                         *
 *****************************************************************************/

    i2c_send(I2C_BUS_1 | 0x1000,       // Bus I2C number = 1
             I2C_ADDR_EEP_W    ,       // Address I2C of the EEPROM
             i2c_tx_frame      ,       // Frame to be transmitted
             0                 ,       // Parameter unused
             -1                ,       // Not called from an automaton
             -1                ,       // EVTS_I2C_END not requested
             0x01              )     ; // Flags:
                                       // - b2 = 0: see b0
                                       // - b1 = 0: block length on 1 byte
                                       // - b0 = 1: "stop" generated only at 
                                       //           the last block end

    return 0                         ; // No error
  }


/*  Procedure eroreq -----------------------------------------------------------

    Purpose : UI treatment - Reads data in EEPROM (offset specified). We call 
              the non blocking procedure i2c_io which reads data in the EEPROM.
    Note    : The EEPROM AT24C08A is used.
*/

static int eroreq(int n)
  {
    I2c_io        *ptr               ; // Pointer on struct describing a frame 
                                       // to be transmitted

/*****************************************************************************
 * Step 1 : Fill the request:                                                *
 * ------   - 1st frame (EEPROM offset):                                     *
 *            - Writing address I2C of the EEPROM                            *
 *            - Flags (b0: "start" generation, b1: "stop" generation, b2:    *
 *              received byte acknowledgement)                               *
 *            - Offset in EEPROM (ex: 0x0322)                                *
 *            - Address of buffer and number of bytes to be transmitted      *
 *          - 2nd frame (wait request):                                      *
 *            - Code for wait request                                        *
 *            - Number of milliseconds to wait                               *
 *          - 3rd frame (reading):                                           *
 *            - Reading address I2C of the EEPROM                            *
 *            - Flags (unused when reading)                                  *
 *            - Reception buffer address and number of bytes to be read      *
 *****************************************************************************/

    ptr        = i2c_iolist          ; // Init of the pointer
    ptr->io    = I2C_ADDR_EEP_W      ; // Writing address I2C of the EEPROM
    ptr->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    i2c_tx[0]  = 0x03                ; // Offset in EEPROM (b8-15)
    i2c_tx[1]  = 0x22                ; // Offset in EEPROM (b0-7)
    ptr->adr   = i2c_tx              ; // Address of buffer to be transmitted
    ptr->lg    = 2                   ; // Number of bytes to be transmitted

    ptr++                            ; // Increment pointer (next frame)
    ptr->io    = 0xFF                ; // Wait request
    ptr->lg    = 5                   ; // Wait 5ms

    ptr++                            ; // Increment pointer (next frame)
    ptr->io    = I2C_ADDR_EEP_R      ; // Reading address I2C of the EEPROM
    ptr->flags = 0                   ; // Flags (unused when reading: "start" 
                                       // & "stop" always generated and 
                                       // acknowledgement managed by dependent 
                                       // hardware part of the I2C)
    ptr->adr   = i2c_rx              ; // Reception buffer address
    ptr->lg    = n                   ; // Number of bytes to be read

/*****************************************************************************
 * Step 2 : Deposit an event to the automaton I2C. This event contains the   *
 * ------   frame to be transmitted.                                         *
 *****************************************************************************/

    i2c_io(I2C_BUS_1           ,       // Bus I2C number = 1
           i2c_iolist          ,       // Buffer containing struct describing 
                                       // frames to be transmitted
           ptr - i2c_iolist + 1,       // Number of struct describing frames
           -1                  ,       // Not called from an automaton
           -1                  )     ; // EVTS_I2C_END not requested

    return 0                         ; // No error
  }


/*  Procedure erreq -----------------------------------------------------------

    Purpose : UI treatment - Reads data in EEPROM (next bytes). We call the 
              non blocking procedure i2c_io which reads data in the EEPROM.
    Note    : The EEPROM AT24C08A is used.
*/

static int erreq(int n)
  {
    I2c_io        *ptr               ; // Pointer on struct describing a frame 
                                       // to be transmitted

/*****************************************************************************
 * Step 1 : Fill the request:                                                *
 * ------   - Reading address I2C of the EEPROM                              *
 *          - Flags (unused when reading)                                    *
 *          - Reception buffer address and number of bytes to be read        *
 *****************************************************************************/

    ptr        = i2c_iolist          ; // Init of the pointer
    ptr->io    = I2C_ADDR_EEP_R      ; // Reading address I2C of the EEPROM
    ptr->flags = 0                   ; // Flags (unused when reading)
    ptr->adr   = i2c_rx              ; // Reception buffer address
    ptr->lg    = n                   ; // Number of bytes to be read

/*****************************************************************************
 * Step 2 : Deposit an event to the automaton I2C. This event contains the   *
 * ------   frame to be transmitted.                                         *
 *****************************************************************************/

    i2c_io(I2C_BUS_1 ,                 // Bus I2C number = 1
           i2c_iolist,                 // Buffer containing struct describing 
                                       // frames to be transmitted
           1         ,                 // Number of struct describing frames
           -1        ,                 // Not called from an automaton
           -1        )               ; // EVTS_I2C_END not requested

    return 0                         ; // No error
  }


/*  Procedure ldreq -----------------------------------------------------------

    Purpose : UI treatment - Display text on screen LCD.
    Note    : The screen LCD160CR is used.
*/

static int ldreq(int n)
  {
    I2c_io        *pio               ; // Pointer on struct describing a frame 
                                       // to be transmitted
    Lcd_text      *ptxt              ; // Pointer on struct describing text to 
                                       // be displayed

/*****************************************************************************
 * Step 1 : Initialize pointers                                              *
 * ------                                                                    *
 *****************************************************************************/

    pio  = i2c_iolist                ; // Init of the pointer on struct 
                                       // describing a frame to be transmitted
    ptxt = &i2c_text[n]              ; // Init of the pointer on struct 
                                       // describing text to be displayed

/*****************************************************************************
 * Step 2 : Fill the request:                                                *
 * ------   - 1st frame (set position):                                      *
 *            - Writing address I2C of the screen LCD                        *
 *            - Flags (b0: "start" generation, b1: "stop" generation, b2:    *
 *              received byte acknowledgement)                               *
 *            - Command "set screen position"                                *
 *            - Horizontal and vertical coordinates                          *
 *            - Address of buffer and number of bytes to be transmitted      *
 *          - 2nd frame (set font):                                          *
 *            - Writing address I2C of the screen LCD                        *
 *            - Flags (b0: "start" generation, b1: "stop" generation, b2:    *
 *              received byte acknowledgement)                               *
 *            - Command "set font"                                           *
 *            - Parameters:                                                  *
 *              0b00ss ssss STff hhvv                                        *
 *                  |       |||  | vertical bold offset                      *
 *                  |       |||  horizontal bold offset                      *
 *                  |       ||font                                           *
 *                  |       |transparency flag (???)                         *
 *                  |       soft scroll flag (???)                           *
 *                  pixel replication (s+1) (???)                            *
 *          - 3rd frame (set colors):                                        *
 *            - Writing address I2C of the screen LCD                        *
 *            - Flags (b0: "start" generation, b1: "stop" generation, b2:    *
 *              received byte acknowledgement)                               *
 *            - Command "set colors"                                         *
 *            - Foreground and background colors                             *
 *          - 4th frame (transmit text to be displayed on screen):           *
 *            - Writing address I2C of the screen LCD                        *
 *            - Flags (b0: "start" generation, b1: "stop" generation, b2:    *
 *              received byte acknowledgement)                               *
 *            - Address of buffer and number of bytes to be transmitted      *
 *****************************************************************************/

    pio        = i2c_iolist          ; // Init of the pointer
    pio->io    = I2C_ADDR_SCR_LCD_W  ; // Writing address I2C of screen LCD
    pio->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    i2c_tx[0]  = 0x02                ; // Mode command
    i2c_tx[1]  = 'X'                 ; // Set position
    i2c_tx[2]  = ptxt->x             ; // Horizontal coordinate
    i2c_tx[3]  = ptxt->y             ; // Vertical coordinate
    pio->adr   = i2c_tx              ; // Address of buffer to be transmitted
    pio->lg    = 4                   ; // Number of bytes to be transmitted

    pio++                            ; // Increment pointer (next frame)
    pio->io    = I2C_ADDR_SCR_LCD_W  ; // Writing address I2C of screen LCD
    pio->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    i2c_tx[4]  = 0x02                ; // Mode command
    i2c_tx[5]  = 'F'                 ; // Set font
    i2c_tx[6]  = 0x00                ; // Parameters (see header)
    i2c_tx[7]  = 0x01                ; //
    pio->adr   = &i2c_tx[4]          ; // Address of buffer to be transmitted
    pio->lg    = 4                   ; // Number of bytes to be transmitted

    pio++                            ; // Increment pointer (next frame)
    pio->io    = I2C_ADDR_SCR_LCD_W  ; // Writing address I2C of screen LCD
    pio->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    i2c_tx[8]  = 0x02                ; // Mode command
    i2c_tx[9]  = 'c'                 ; // Set colors
    memcpy(&i2c_tx[10], &ptxt->fc, 2); // Foreground color
    memcpy(&i2c_tx[12], &ptxt->bc, 2); // Background color
    pio->adr   = &i2c_tx[8]          ; // Address of buffer to be transmitted
    pio->lg    = 6                   ; // Number of bytes to be transmitted

    pio++                            ; // Increment pointer (next frame)
    pio->io    = I2C_ADDR_SCR_LCD_W  ; // Writing address I2C of screen LCD
    pio->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    strcpy((char*)&i2c_tx[14],         // Copy string to display
           ptxt->txt        )        ; //
    pio->adr   = &i2c_tx[14]         ; // Address of buffer to be transmitted
    pio->lg    = sizeof(ptxt->txt) + 1;// Number of bytes to be transmitted

/*****************************************************************************
 * Step 3 : Deposit an event to the automaton I2C. This event contains the   *
 * ------   frame to be transmitted.                                         *
 *****************************************************************************/

    i2c_io(I2C_BUS_1           ,       // Bus I2C number = 1
           i2c_iolist          ,       // Buffer containing struct describing 
                                       // frames to be transmitted
           pio - i2c_iolist + 1,       // Number of struct describing frames
           -1                  ,       // Not called from an automaton
           -1                  )     ; // EVTS_I2C_END not requested

    return 0                         ; // No error
  }


/*  Procedure lcreq -----------------------------------------------------------

    Purpose : UI treatment - Clear screen LCD.
    Note    : The screen LCD160CR is used.
*/

static int lcreq(int n)
  {
    I2c_io        *ptr               ; // Pointer on struct describing a frame 
                                       // to be transmitted

/*****************************************************************************
 * Step 1 : Fill the request:                                                *
 * ------   - Writing address I2C of the screen LCD                          *
 *          - Flags (b0: "start" generation, b1: "stop" generation, b2:      *
 *            received byte acknowledgement)                                 *
 *          - Command "clear screen LCD"                                     *
 *          - Address of buffer and number of bytes to be transmitted        *
 *****************************************************************************/

    ptr        = i2c_iolist          ; // Init of the pointer
    ptr->io    = I2C_ADDR_SCR_LCD_W  ; // Writing address I2C of screen LCD
    ptr->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    i2c_tx[0]  = 0x02                ; // Mode command
    i2c_tx[1]  = 'E'                 ; // Clear screen LCD
    ptr->adr   = i2c_tx              ; // Address of buffer to be transmitted
    ptr->lg    = 2                   ; // Number of bytes to be transmitted

/*****************************************************************************
 * Step 2 : Deposit an event to the automaton I2C. This event contains the   *
 * ------   frame to be transmitted.                                         *
 *****************************************************************************/

    i2c_io(I2C_BUS_1 ,                 // Bus I2C number = 1
           i2c_iolist,                 // Buffer containing struct describing 
                                       // frames to be transmitted
           1         ,                 // Number of struct describing frames
           -1        ,                 // Not called from an automaton
           -1        )               ; // EVTS_I2C_END not requested

    return 0                         ; // No error
  }


/*  Procedure ltcreq ----------------------------------------------------------

    Purpose : UI treatment - Get touch coordinates.
    Note    : The screen LCD160CR is used.
*/

static int ltcreq(int n)
  {
    I2c_io        *ptr               ; // Pointer on struct describing a frame 
                                       // to be transmitted

/*****************************************************************************
 * Step 1 : Fill the request:                                                *
 * ------   - 1st frame (transmit command):                                  *
 *            - Writing address I2C of the screen LCD                        *
 *            - Flags (b0: "start" generation, b1: "stop" generation, b2:    *
 *              received byte acknowledgement)                               *
 *            - Command "get touch coordinates"                              *
 *            - Address of buffer and number of bytes to be transmitted      *
 *          - 2nd frame (wait request):                                      *
 *            - Code for wait request                                        *
 *            - Number of milliseconds to wait                               *
 *          - 3rd frame (position reading):                                  *
 *            - Reading address I2C of the screen LCD                        *
 *            - Flags (unused when reading)                                  *
 *            - Reception buffer address and number of bytes to be read      *
 *            - Received bytes:                                              *
 *              - 1st byte = 0x03: ???                                       *
 *              - 2nd byte: b7 = 0: screen not currently touched             *
 *                               1: screen currently touched                 *
 *              - 3rd byte: vertical coordinate*                             *
 *              - 4th byte: horizontal coordinate*                           *
 *                *: "Global screen orientation applies also to touch        *
 *                   coordinates."                                           *
 *****************************************************************************/

    ptr        = i2c_iolist          ; // Init of the pointer
    ptr->io    = I2C_ADDR_SCR_LCD_W  ; // Writing address I2C of screen LCD
    ptr->flags = 0x03                ; // b0 = 1: generate "start"
                                       // b1 = 1: generate "stop"
                                       // b2 = 0: acknowledge received bytes 
                                       //         (NA for writing)
    i2c_tx[0]  = 0x02                ; // Mode command
    i2c_tx[1]  = 'T'                 ; // Get touch coordinates
    ptr->adr   = i2c_tx              ; // Address of buffer to be transmitted
    ptr->lg    = 2                   ; // Number of bytes to be transmitted

    ptr++                            ; // Increment pointer (next frame)
    ptr->io    = 0xFF                ; // Wait request
    ptr->lg    = 1                   ; // Wait 1ms

    ptr++                            ; // Increment pointer (next frame)
    ptr->io    = I2C_ADDR_SCR_LCD_R  ; // Reading address I2C of screen LCD
    ptr->flags = 0                   ; // Flags (unused when reading: "start" 
                                       // & "stop" always generated and 
                                       // acknowledgement managed by dependent 
                                       // hardware part of the I2C)
    ptr->adr   = i2c_rx              ; // Reception buffer address
    ptr->lg    = 4                   ; // Number of bytes to be read

/*****************************************************************************
 * Step 2 : Deposit an event to the automaton I2C. This event contains the   *
 * ------   frame to be transmitted.                                         *
 *          Note: The automaton number and logical channel number are        *
 *                specified as the event EVTS_I2C_END will be deposit when   *
 *                reading is ended.                                          *
 *****************************************************************************/

    i2c_io(I2C_BUS_1           ,       // Bus I2C number = 1
           i2c_iolist          ,       // Buffer containing struct describing 
                                       // frames to be transmitted
           ptr - i2c_iolist + 1,       // Number of struct describing frames
           AUT_TASK            ,       // Automaton number
           myapp_taskid && 0xFF)     ; // Logical channel number

    return 0                         ; // No error
  }


/*  Procedure sapt ------------------------------------------------------------

    Purpose : UI treatment - Set a new ASY1/2 transmit pattern
*/

static int sapt(int n)
  {
    fill256(buf_snd[n == VASY1PT ? 1 : 2],cfg[n]) ;

    return 0                         ; // No error
  }


/*  Procedure sdpt ------------------------------------------------------------

    Purpose : UI treatment - Set a new DAC transmit pattern
*/

static int sdpt(int n)
  {

    return 0                         ; // No error
  }

    
/*  Procedure srcu ------------------------------------------------------------

    Purpose : UI treatment - Set a new input according to VINPUT and a new RCU
              according to VNUMRCU.
*/

static int srcu(int n)
  {
    INT           ret                ; // Error code

    set_rcu(rcurcv_id              ,   // id     = the indicated receiver
            cfg[VINPUT]            ,   // swpos  = the selected input (IR/UHF)
            str_rcuna[cfg[VNUMRCU]],   // name   = the selected RCU name
            &ret                    ); // ret    = error code

    return ret                       ; // No error
  }


/*  Procedure scap ------------------------------------------------------------

    Purpose : UI treatment - Starts a RCU symbols capture.
*/

static int scap(int n)
  {
    INT           ret                ; // Error code

    capture_rcu(rcurcv_id    ,         // id     = the indicated receiver
                cfg[VONMIN]  ,         // otmin  = start on time min duration
                cfg[VONMAX]  ,         // otmax  = start on time max duration
                cfg[VSTMIN]  ,         // stmin  = start symb time min duration
                cfg[VSTMAX]  ,         // stmax  = start symb time max duration
                &ret          )      ; // ret    = error code

    return ret                       ; // No error
  }


/*  Procedure sret ------------------------------------------------------------

    Purpose : UI treatment - Affects parameter to "condret".
*/

static int sret(int n)
  {
    condret = n                      ; // Affects parameter to "condret"
    return 0                         ; // No error
  }


/*  Procedure scon ------------------------------------------------------------

    Purpose : UI treatment - Affects "condret" to "condcode".
*/

static int scon(int n)
  {
    condcode = condret               ; // Affects "condret" to "condcode"
    return 0                         ; // No error
  }


/*  Procedure rest ------------------------------------------------------------

    Purpose : UI treatment - Set to 1 the "flag_rest" global variable. The
              UI main event loop (step 6 of "myproc_task") checks this flag
              a each loop end.

*/

static int rest(int n)
  {
    flag_rest = 1                    ; // Ask for an end of loop
    return 0                         ; // No error
  }


/*  Procedure ev_opt ----------------------------------------------------------

    Purpose : Convert the EV_ code event to a OPT_ menu event.
*/

static int ev_opt(int ev)
  {
    int             opt              ; // Return code

    switch ( ev )
      {
        case EV_ASY0_RCV             : // Keyboard console data received
          opt = ev_asy0_rcv()        ; // Determine option selected
          break                      ;

        case EV_ASY1_RCV             : // Data received on ASY\DEV1
          opt = ev_asy12_rcv(1)      ; // Determine option selected
          break                      ;

        case EV_ASY2_RCV             : // Data received on ASY\DEV2
          opt = ev_asy12_rcv(2)      ; // Determine option selected
          break                      ;

        case EV_ASY0_SND             : // Data sent on ASY\DEV0
          opt = ev_asy0_snd()        ; // Determine option selected
          break                      ;

        case EV_ASY1_SND             : // Data sent on ASY\DEV1
          opt = ev_asy12_snd(1)      ; // Determine option selected
          break                      ;

        case EV_ASY2_SND             : // Data sent on ASY\DEV2
          opt = ev_asy12_snd(2)      ; // Determine option selected
          break                      ;

        case EV_GPIO0_IN             : // GPIO input pin change button 1
          opt = ev_gpio012_in(0)     ; // Determine option selected
          break                      ;

        case EV_GPIO1_IN             : // GPIO input pin change button 2
          opt = ev_gpio012_in(1)     ; // Determine option selected
          break                      ;

        case EV_GPIO2_IN             : // GPIO input pin change button 3
          opt = ev_gpio012_in(2)     ; // Determine option selected
          break                      ;

        case EV_I2C_END              : // I2C job ended
          opt = ev_i2c_end()         ; // Determine option selected
          break                      ;

        case EV_KBD_RCV              : // Keyboard event received from RCU
          opt = ev_kbd_rcv()         ; // Determine option selected
          break                      ;

        case EV_MSEC                 : // 1000 sec have been elapsed
          opt = ev_msec()            ; // Determine option selected
          break                      ;
      }

    return opt                       ;
  }


/*  Procedure ev_asy0_rcv -----------------------------------------------------

    Purpose : Parses the RESP_READ event for DEV_ASY0 and then selects the 
              treatment when EV_ASY0_RCV event is received. Also, as a receive
              token has been used, give to the ASY driver a new receive token.
*/

static int ev_asy0_rcv(void)
  { 
    unsigned char  *buf              ; // Buffer address
    char            c                ; // First character of buffer
    int             ret              ; // Return code
    int             err              ; // Error code

/*****************************************************************************
 * Step 1 : Update the number of tokens. We just receive one RESP_READ       *
 * ------   event from ASY device DEV0, so we decrement "tok_rcv[0]" counter.*
 *          We also initialize "ret" to ignore the event.                    *
 *****************************************************************************/

    tok_rcv[0] --                    ; // One token less.
    ret = OPT_IGNO                   ; // This character will be ignored


/*****************************************************************************
 * Step 2 : Get the data buffer address. We have just received a READ_RESP   *
 * ------   event. A copy is available in the "task_evt" global variable     *
 *          (type is S_"evt"). The buffer address is contained in the        *
 *          "adresse" field of the "task_evt" event structure. If no buffer  *
 *          has been received, we jump to step 6 to give a new reveive       *
 *          token.                                                           *
 *****************************************************************************/

    buf = task_evt.adresse           ; // Read buffer address from event
    if (buf EQ HNULL)                  // If no buffer received,
      goto step6                     ; //   go to step 6.


/*****************************************************************************
 * Step 3 : Check for errors. Field "length" of "task_evt" global variable   *
 * ------   is an error code if negative. In case of error, we jump to step  *
 *          5 to free the received buffer.                                   *
 *****************************************************************************/

    err = task_evt.longueur          ; // Read error code from event
    if (err < 0)                       // If an error occured,
      goto step5                     ; //   go to step 5.


/*****************************************************************************
 * Step 4 : Parse the content of the received buffer. Here we implement a    *
 * ------   very naive parsing. We extract the first received character and  *
 *          we just check it is an ASCII code:                               *
 *          - Codes  '0' to '9' are converted to OPT_0 to OPT_9.             *
 *          - Codes  'A' to 'D' are converted to OPT_BUT1 to OPT_BUT4.       *
 *          - Codes  'a' to 'd' are converted to OPT_BUT1 to OPT_BUT4.       *
 *          - Code   '\r'       is  converted to OPT_ENTER.                  *
 *          - Code   8          is  converted to OPT_BACK.                   *
 *          - With other values, we return OPT_INVALID.                      *
 *****************************************************************************/

    c   = (char) buf[0]              ; // Extract the first byte of data

    if      (c >= '0' && c <= '9')     // If byte is character '0' to '9'
      ret = OPT_0    + (c - '0')     ; // then return OPT_<c>         

    else if (c >= 'A' && c <= 'D')     // If byte is character 'A' to 'D'
      ret = OPT_BUT1 + (c - 'A')     ; // then return OPT_BUT1/2/3/4

    else if (c >= 'a' && c <= 'd')     // If byte is character 'a' to 'd'
      ret = OPT_BUT1 + (c - 'a')     ; // then return OPT_BUT1/2/3/4

    else if (c == '\r'           )     // If byte is character '\r'       
      ret = OPT_ENTER                ; // then return OPT_ENTER

    else if (c == 8              )     // If byte is character BACKSPACE
      ret = OPT_BACK                 ; // then return OPT_ENTER

    else if (c == 27             )     // If byte is character ESCAPE
      ret = OPT_ESC                  ; // then return OPT_ESC

    else
      ret = OPT_INVALID              ; // This character will be ignored


/*****************************************************************************
 * Step 5 : We have finished to use the buffer data, we have now to free     *
 * ------   that buffer. If we forget that, the STM32 will quickly get out   *
 *          of memory (a STM32re has only 80 KB of RAM).                     *
 *****************************************************************************/

    step5                            : // Step 5 label
    free_buf(buf, &err)              ; // Free the buffer.


/*****************************************************************************
 * Step 6 : Give a new receive token to the ASY driver for device DEV0.      *
 * ------                                                                    *
 *****************************************************************************/

    step6                            : // Step 6 label
    myio_givetok(asy_iod0   ,          // I/O descriptor
                 1          ,          // Number of receive token
                 1          ,          // Size of receive buffer
                 &tok_rcv[0]  )      ; // Counter of given tokens

    return ret                       ;
  }


/*  Procedure ev_asy0_snd -----------------------------------------------------

    Purpose : Select the treatment when EV_ASY0_SND event is received.
*/

static int ev_asy0_snd(void)
  { 
    return OPT_IGNO                  ; // Ignore these events
  }


/*  Procedure ev_asy12_rcv -----------------------------------------------------

    Purpose : Select the treatment when EV_ASY1_RCV/EV_ASY2_RCV  event is 
              received. The "n" input parameter value is 1 for ASY1 and 2 for
              ASY2.
*/

static int ev_asy12_rcv(int n)
  { 
    unsigned char  *buf              ; // Buffer address
    int             len              ; // Data length
    int             vspeed           ; // VPEED1 or VSPEED2
    int             vasy             ; // VASY1  or VASY2
    int             err              ; // Return code

/*****************************************************************************
 * Step 1 : Update the number of tokens. We just receive one RESP_READ       *
 * ------   event from ASY device DEV1/DEV2, so we decrement "tok_rcv[1]" or *
 *          "tok_rcv[2]" counter.                                            *
 *****************************************************************************/

    tok_rcv[n] --                    ; // One token less.


/*****************************************************************************
 * Step 2 : Get the data buffer address. We have just received a READ_RESP   *
 * ------   event. A copy is available in the "task_evt" global variable     *
 *          (type is "S_evt"). The buffer address is contained in the        *
 *          "adresse" field of the "task_evt" event structure. Length of     *
 *          received data is available in the "longueur" field.              *
 *****************************************************************************/

    buf = task_evt.adresse           ; // Read buffer address from event
    len = task_evt.longueur          ; // Read data length from event


/*****************************************************************************
 * Step 3 : If we are in fast mode, we give tokens to ASY device DEV1 or     *
 * ------   DEV2, so that it has always 2 tokens available for reception. In *
 *          slow mode, 1 token will be given when "ev_msec" is called in at  *
 *          most 1 second from now.                                          *
 *****************************************************************************/

    vspeed = (n == 1) ? VSPEED1 : 
                        VSPEED2      ;

    vasy   = (n == 1) ? VASY1   : 
                        VASY2        ;

    if ( cfg[vspeed] EQ CFG_FAST )     // If in fast mode,
      while ( tok_rcv[n] LT 2 )        // give at most 2 tokens.
        myio_givetok(iods[vasy] ,      // I/O descriptor
                     1          ,      // Number of receive token
                     LGRCV12    ,      // Size of receive buffer
                     &tok_rcv[n]  )  ; // Counter of given tokens


/*****************************************************************************
 * Step 4 : If no buffer has been received, exit the procedure. If length is *
 * ------   negative, an error occured: free the buffer and exit.            *
 *****************************************************************************/

    if (buf EQ HNULL)                  // If no buffer received,
      goto end                       ; //   exit the procedure.

    if (len < 0)                       // If an error occured, received
      BEGIN                            // data are incorrect.
        free_buf(buf, &err)          ; // Free the buffer.
        goto end                     ; // Exit from the procedure.
      END_IF                           //


/*****************************************************************************
 * Step 5 : Test state of ASY1. If currently sending, free the telecom       *
 * ------   buffer and exit. If we forget to free the buffer, the STM32 will *
 *          quickly get out of memory (a STM32re has only 80 KB of RAM).     *
 *****************************************************************************/

    if (cfg[vasy] EQ 1)                // If broadcasting enabled,    
      BEGIN                            // we cannot send.
        free_buf(buf, &err)          ; // Free the buffer.
        goto end                     ; // Exit from the procedure.
      END_IF                           // 


/*****************************************************************************
 * Step 6 : Output is available, we send back the received data as an "echo".*
 * ------   We do not unschedule.                                            *
 *****************************************************************************/

    myio_send   (iods[vasy] ,          // I/O descriptor
                 buf        ,          // Address of buffer
                 len        ,          // Number of bytes to send
                 &cnt_snd[n] )       ; // Number of sending messages

    end                              : // Early exit

    return OPT_IGNO                  ; // Ignore these events
  }


/*  Procedure ev_asy12_snd -----------------------------------------------------

    Purpose : Select the treatment when EV_ASY1_SND/EV_ASY2_SND event is 
              received. The "n" input parameter is 1 for ASY1 and 2 for ASY2.
*/

static int ev_asy12_snd(int n)
  { 

    unsigned char  *snd              ; // Buffer address
    int             vasy             ; // VASY1 if AYS1, VASY2 if ASY2
    int             ret              ; // Return code

/*****************************************************************************
 * Step 1 : One message has been sent, update the number of messages given   *
 * ------   to ASY driver.                                                   *
 *****************************************************************************/

    cnt_snd[n] --                    ; // One less message in driver's queue


/*****************************************************************************
 * Step 2 : If sent buffer is not the static "buf_snd[1/2]" buffer, it is an *
 * ------   "echo" buffer sent in "ev_asy12_rcv". We need to free it.        *
 *****************************************************************************/

    snd = task_evt.adresse           ; // Get sent buffer address

    if ( snd NE buf_snd[n] )           // If an "echo" buffer has been sent,
      free_buf(snd, &ret)            ; // free it.


/*****************************************************************************
 * Step 2 : If stop is requested, there is nothing more to do.               *
 * ------                                                                    *
 *****************************************************************************/

    vasy = (n == 1) ? VASY1 : VASY2  ; // Associated state variable number

    if (cfg[vasy] == 0)                // If stop has been requested,
      goto end                       ; // exit from the procedure.


/*****************************************************************************
 * Step 3 : When running, we want to have at least two messages ready to be  *
 * ------   sent in ASY driver so that there is no time lost between two     *
 *          messages. Variable "msg_snd[n]" holds the number of messages     *
 *          given to ASY driver. This variable is incremented each time a    *
 *          request is sent to ASY driver and decremented each time a        *
 *          response is received from ASY driver (Step 1).                   *
 *****************************************************************************/

    while (cnt_snd[n] LT 2)            // Until we have sent enough messages,
      myio_send   (iods[vasy] ,        // I/O descriptor
                   buf_snd[n] ,        // Address of buffer
                   256      ,          // Number of bytes to send
                   &cnt_snd[n])      ; // Number of sending messages

    end                              : // Early exit
    return OPT_IGNO                  ; // Ignore these events
  }


/*  Procedure ev_gpio012_in ---------------------------------------------------

    Purpose : Parse the received EV_GPIO0/1/2_IN event and select the treatment
              to be executed.
*/

static int ev_gpio012_in(int n)
  {

/*****************************************************************************
 * Step 1 : The event we received is an IND_REPORT. Its "res2" field is the  *
 * ------   tag identifier and its "longueur" field the new tag value.       *
 *          In GPIO case, we only expect IND_REPORT events on tag INPUT      *
 *          indicating the button has been pressed or released. When INPUT   *
 *          is LOW (0), the button has been pressed and this corresponds to  *
 *          option BUT1. When INPUT is high (1), the button has been         *
 *          released and we want to ignore.                                  *
 *****************************************************************************/

    if (task_evt.longueur EQ 0)        // If INPUT is LOW,
      return OPT_BUT1 + n            ; //   button 1 has been pressed.
    else                               // Otherwise,
      return OPT_IGNO                ; //   it has been released
  }


/*  Procedure ev_i2c_end ------------------------------------------------------

    Purpose : Display the touch coordinates.
*/

static int ev_i2c_end(void)
  {

/*****************************************************************************
 * Step 1 : The received event is EVTS_I2C_END.                              *
 * ------   Return the option IEC END                                        *
 *****************************************************************************/

    return OPT_I2C_END               ; // Return option IEC END
  }


/*  Procedure ev_kbd_rcv ------------------------------------------------------

    Purpose : Select the treatment when EV_KBD_RCV event is received. The event
              that we received is stored in "task_evt", from which we can get
              the original event code (DRV_KEY_PRESS or DRV_KEY_RELEASE) and
              the keycode (task_evt.reserve).
*/

static int ev_kbd_rcv(void)
  { 

/*****************************************************************************
 * Step 1 : The KEYBOARD handler sends us two different event codes that are *
 * ------   DRV_KEY_PRESS when a key is pressed and DRV_KEY_RELEASE when the *
 *          key is reselased. Here we chose to use the DRV_KEY_PRESS and to  *
 *          ignore all the DRV_KEY_REALEASE. So we test if the event code is *
 *          a DRV_KEY_RELEASE and we exit immedialtely in that case.         *
 *****************************************************************************/

    if (task_evt.code                  // We just ignore all the "release key"
          EQ DRV_KEY_RELEASE)          // events. We return the OPT_IGNO
      return OPT_IGNO                ; // code.

/*****************************************************************************
 * Step 2 : Here we have an event code DRV_KEY_PRESS. The "reserve" field is *
 * ------   the key-code. The codes are KEY_0 to KEY_9 for RCU digit keys.   *
 *          For RCU colored keys RED, GREEN, YELLOW and BLUE key codes are   *
 *          respectively KEY_F1, KEY_F2, KEY_F3 and KEY_F4.                  *
 *****************************************************************************/

    switch (task_evt.reserve)
      {
        case KEY_0                   : // Digit keys, 0 to 9
          return OPT_0               ;
        case KEY_1                   :
          return OPT_1               ;
        case KEY_2                   :
          return OPT_2               ;
        case KEY_3                   :
          return OPT_3               ;
        case KEY_4                   :
          return OPT_4               ;
        case KEY_5                   :
          return OPT_5               ;
        case KEY_6                   :
          return OPT_6               ;
        case KEY_7                   :
          return OPT_7               ;
        case KEY_8                   :
          return OPT_8               ;
        case KEY_9                   :
          return OPT_9               ;

        case KEY_ENTER               : // OK key
          return OPT_ENTER           ;
        case KEY_BACK                : // BACK key
          return OPT_BACK            ;
        case KEY_EXIT                : // EXIT key
          return OPT_ESC             ;

        case KEY_F1                  : // RED key
          return OPT_BUT1            ;
        case KEY_F2                  : // GREEN KEY
          return OPT_BUT2            ;
        case KEY_F3                  : // YELLOW key
          return OPT_BUT3            ;
        case KEY_F4                  : // BLUE key
          return OPT_BUT4            ;

        case KEY_CAPTURE             : // Pseudo key for CAPTURE
          return OPT_CAPT            ;

        default                      :
          return OPT_INVALID         ;
      }
  }


/*  Procedure ev_msec ---------------------------------------------------------

    Purpose : Select the option when EV_MSEC event is received.
*/

static int ev_msec(void)
  { 

/*****************************************************************************
 * Step 1 : If we are in slow mode, we give tokens to ASY device DEV1, so    *
 * ------   that it has at least 1 token available for reception. In fast    *
 *          mode, at most 2 tokens are given by "ev_asy12_rcv" as soon as a  *
 *          RESP_READ event is received.                                     *
 *****************************************************************************/

    if ( cfg[VSPEED1] EQ CFG_SLOW )    // If in slow mode,
      while ( tok_rcv[1] LT 1 )        // give at most 1 token.
        myio_givetok(asy_iod1 ,        // I/O descriptor
                     1        ,        // Number of receive tokens
                     LGRCV12  ,        // Size of receive buffer
                     &tok_rcv[1]  )  ; // Counter of given tokens

/*****************************************************************************
 * Step 2 : Same thing as above but with ASY2.                               *
 * ------                                                                    *
 *****************************************************************************/

    if ( cfg[VSPEED2] EQ CFG_SLOW )    // If in slow mode,
      while ( tok_rcv[2] LT 1 )        // give at most 1 token.
        myio_givetok(asy_iod2 ,        // I/O descriptor
                     1        ,        // Number of receive tokens
                     LGRCV12  ,        // Size of receive buffer
                     &tok_rcv[2]  )  ; // Counter of given tokens


/*****************************************************************************
 * Step 3 : Option selected is always the system option OPT_TIME used to     *
 * ------   update time display.                                             *
 *****************************************************************************/

    return OPT_TIME                  ; // Return time refresh option
  }


/*  Procedure wait_myevents ---------------------------------------------------

    Purpose : Unschedule until any of my events is received or "msec" seconds
              have been elapsed. A value of 0 for "msec" means no maximum
              time limit. According to the received event, the return value
              of this procedure is as follows:

              Code             Reserve        Return value
              ---------------  --------       ---------------
              RESP_READ        asy_iod0   ->  EV_ASY0_RCV   0
              RESP_READ        asy_iod1   ->  EV_ASY1_RCV   1
              RESP_READ        asy_iod2   ->  EV_ASY2_RCV   2
              RESP_WRITE       asy_iod0   ->  EV_ASY0_SND   3
              RESP_WRITE       asy_iod1   ->  EV_ASY1_SND   4
              RESP_WRITE       asy_iod2   ->  EV_ASY2_SND   5
              IND_REPORT       gpio_iod1  ->  EV_GPIO0_IN   10
              IND_REPORT       gpio_iod2  ->  EV_GPIO1_IN   11
              IND_REPORT       gpio_iod3  ->  EV_GPIO2_IN   12
              EVTS_I2C_END     i2c1       ->  EV_I2C_END    20
              DRV_KEY_PRESS    any        ->  EV_KBD_RCV    30
              DRV_KEY_RELEASE  any        ->  EV_KBD_RCV    30
              TICK             any        ->  EV_MSEC       40

              The "ret" value maybe -1 if we receive something else
*/


static int wait_myevents(void)
  {
    int             waitlist[7][3]   ; // Parameter of "waitevt_task"
    int             res              ; // Field "reserve" of task_evt.reserve
    int             ret              ; // Return code

/*****************************************************************************
 * Step 1 : Build the list of our awaited events and then unschedule until   *
 * ------   one of those is received. The events we are waiting for are:     *
 *          - The RESP_READ. Those are coming from the ASY driver when data  *
 *            is received on serial controller DEV0, DEV1 or DEV2. So the    *
 *            awaited code event is RESP_READ and the awaited "reserve"      *
 *            field value may be "asy_iod0", "asy_iod1" or "asy_iod2". We    *
 *            choose to use -1 (any value) for "reserve".                    *
 *          - The RESP_WRITE. Those are coming from the ASY driver when send *
 *            request has been completed (the last byte has been sent). The  *
 *            code event is RESP_READ and the "reserve" field value may be   *
 *            "asy_iod0", "asy_iod1" or "asy_iod2". We choose to use -1 (any *
 *            value).                                                        *
 *          - The IND_REPORT. Those are coming from the GPIO driver when we  *
 *            have a change state of any input pin. The "reserve" field may  *
 *            be "gpio_iod1" (RED led) "gpio_iod2" (GREEN led) or            *
 *            "gpio_iod3" (YELLOW led).                                      *
 *          - The DRV_KEY_PRESS (RCU key has been pressed) or the            *
 *            DRV_KEY_RELEASE (RCU key has been released).                   *
 *          - The EVTS_I2C_END. Those are coming from the I2C driver when    *
 *            data is received on the I2C (today only when the touch         *
 *            coordinates have been received).                               *
 *          - The TICK event that is received every second. This one is the  *
 *            result of the call to the "set_tto" procedure.                 *
 *          Then we call the "waitevt_task" procedure that will unschedule   *
 *          us until one of the awaited event will be received.              *
 *****************************************************************************/

    waitlist[0][0]  = WAIT_CODERES   ; // All events with 
    waitlist[0][1]  = RESP_READ      ; // an event code RESP_READ
    waitlist[0][2]  = -1             ; // whatever the value of "reserve" is

    waitlist[1][0]  = WAIT_CODERES   ; // All events with 
    waitlist[1][1]  = RESP_WRITE     ; // an event code RESP_WRITE
    waitlist[1][2]  = -1             ; // whatever the value of "reserve" is

    waitlist[2][0]  = WAIT_CODERES   ; // All events with
    waitlist[2][1]  = IND_REPORT     ; // an event code IND_REPORT
    waitlist[2][2]  = -1             ; // whatever the value of "reserve" is

    waitlist[3][0]  = WAIT_CODERES   ; // All events with
    waitlist[3][1]  = DRV_KEY_PRESS  ; // an event code DRV_KEY_PRESS
    waitlist[3][2]  = -1             ; // whatever the value of "reserve" is

    waitlist[4][0]  = WAIT_CODERES   ; // All events with
    waitlist[4][1]  = DRV_KEY_RELEASE; // an event code DRV_KEY_RELEASE
    waitlist[4][2]  = -1             ; // whatever the value of "reserve" is

    waitlist[5][0]  = WAIT_CODERES   ; // All events with
    waitlist[5][1]  = EVTS_I2C_END   ; // an event code EVTS_I2C_END
    waitlist[5][2]  = -1             ; // whatever the value of "reserve" is

    waitlist[6][0]  = WAIT_CODERES   ; // All events with
    waitlist[6][1]  = TICK           ; // an event code TICK
    waitlist[6][2]  = 0              ; // with "reserve" equal to request

    waitevt_task(waitlist ,            // Address of waiting list
                 7        ,            // Size of "waitlist[]"
                 0        ,            // Maximum waiting time = no
                 0        ,            // Do not purge previous events
                 &ret       )        ; // Return code


/*****************************************************************************
 * Step 2 : Here we are scheduled again. The VMK has written into its global *
 * ------   variable "task_evt" a copy of the event that has scheduled us    *
 *          again. As a code event value, we can have RESP_READ, RESP_WRITE, *
 *          IND_REPORT, EVTS_I2C_END, DRV_KEY_PRESS and DRV_KEY_RELEASE but  *
 *          also the VMK generated event TICK (every second). According to   *
 *          the value of "task_evt.code" and "task_evt.reserve" we compute   *
 *          the return value "ret". If we receive something we are not       *
 *          expecting, we will return a default value of -1.                 *
 *****************************************************************************/

    res = task_evt.reserve           ; // Extract the "reserve" field from the
    ret = -1                         ; // received event.

    switch ( task_evt.code )
      {
        case RESP_READ               : // For a RESP_READ, the "reserve"
          if      (res EQ asy_iod0)    // field is the "iod" value. So
            ret = EV_ASY0_RCV        ; // we compare it to our IOD's,
          else if (res EQ asy_iod1)    // that are "asy_iod0" for ASY\DEV0
            ret = EV_ASY1_RCV        ; // "asy_iod1" for ASY\DEV1 and then
          else if (res EQ asy_iod2)    // "asy_iod2" for ASY\DEV2
            ret = EV_ASY2_RCV        ; //
          break                      ; //

        case RESP_WRITE              : // For a RESP_WRITE, the "reserve"
          if      (res EQ asy_iod0)    // field is the "iod" value. So
            ret = EV_ASY0_SND        ; // we compare it to our IOD's,
          else if (res EQ asy_iod1)    // that are "asy_iod0" for ASY\DEV0,
            ret = EV_ASY1_SND        ; // "asy_iod1" for ASY\DEV1 and
          else if (res EQ asy_iod2)    // then "asy_iod2" for ASY\DEV2
            ret = EV_ASY2_SND        ; //
          break                      ; //

        case IND_REPORT              : // For an IND_REPORT, the "reserve" field
          if      (res EQ gpio_iod1)   // value is the "iod". So we compare
            ret = EV_GPIO0_IN        ; // it to all the IOD's that may send us
          else if (res EQ gpio_iod2)   // an IND_REPORT, so here the 3 buttons,
            ret = EV_GPIO1_IN        ; // so the values may be "gpio_iod1",
          else if (res EQ gpio_iod3)   // "gpio_iod2" or "gpio_iod3" and not
            ret = EV_GPIO2_IN        ; // more
          break                      ; //

        case EVTS_I2C_END            : // For an EVTS_I2C_END, the "reserve"
          ret = EV_I2C_END           ; // field is a copy of the one received
          break                      ; // and we don't care.

        case TICK                    : // For a TICK, the "reserve"
          ret = EV_MSEC              ; // field is a copy of the one received
          break                      ; // and we don't care.

        case DRV_KEY_PRESS           : // For a DRV_KEY_PRESS, the "reserve"
          ret = EV_KBD_RCV           ; // field is the code of the key that
          break                      ; // has been pressed.

        case DRV_KEY_RELEASE         : // For a DRV_KEY_RELEASE, the "reserve"
          ret = EV_KBD_RCV           ; // field is the code of the key that
          break                      ; // has been released.

        default                      : // This should never occur if there
          break                      ; // is no bug.
      }

    return ret                       ;
   }


/*  Procedure fill256 ---------------------------------------------------------

    Purpose : Fill a buffer with a 256 bytes pattern.
*/

static void fill256(unsigned char *buf, int pat)
  {
    int             i                ; // Loop counter

    switch ( pat )
      {
        case 0                       : // 256 bytes 0x00 to 0xFF
          for(i = 0; i < 256; i++)
            *buf++ = i               ;
          break                      ;

        case 1                       : // 256 bytes 0xFF to 0x00
          for(i = 0; i < 256; i++)
            *buf++ = 255 - i         ;
          break                      ;

        case 2                       : // 256 bytes 0x00
          Memset(buf,0x00,256)       ;
          break                      ;

        case 3                       : // 256 bytes 0xAA
          Memset(buf,0xAA,256)       ;
          break                      ;

        case 4                       : // 256 bytes 0xFF
          Memset(buf,0xFF,256)       ;
          break                      ;
      }
  }