浏览代码

Added original sources.

Vladimir N. Shilov 7 年之前
父节点
当前提交
938335a1b7
共有 14 个文件被更改,包括 5014 次插入0 次删除
  1. 327 0
      src/LabArm.c
  2. 143 0
      src/LabArm.h
  3. 465 0
      src/LabArm.uvproj
  4. 483 0
      src/LabArm.uvprojx
  5. 289 0
      src/STM32F10x.s
  6. 504 0
      src/calibration.c
  7. 299 0
      src/clock.c
  8. 207 0
      src/config.c
  9. 577 0
      src/event.c
  10. 61 0
      src/event.h
  11. 390 0
      src/graph.c
  12. 406 0
      src/n3310.c
  13. 62 0
      src/n3310.h
  14. 801 0
      src/ui.c

+ 327 - 0
src/LabArm.c

@@ -0,0 +1,327 @@
+#include "event.h"
+#include "n3310.h"
+#include "LabArm.h"
+
+
+uint16_t AdcOutArray[ADC_ARRAY_SIZE];
+static uint32_t ADCVoltageSum;
+static uint32_t ADCCurrentSum;
+static uint8_t DMACounter;
+uint32_t ADCVoltage;
+uint32_t ADCCurrent;
+
+#if defined(__GNUC__)
+void DMA1_Channel1_IRQHandler()
+#else
+void DMAChannel1_IRQHandler(void) /* Read ADC values */
+#endif
+{
+  if (DMA1->ISR & DMA_ISR_HTIF1 && /*   Half Transfer complete */
+       (DMA1_Channel1->CCR & DMA_CCR1_HTIE) /* Half traisfer interrupt enabled */
+      ) 
+  {
+    int i;
+    /* HTIF is set every time when transfer interrupt > half !!! */
+    DMA1_Channel1->CCR &= ~DMA_CCR1_HTIE; /* disable Half traisfer interrupt !!!! */
+
+    for (i = 0; i < ADC_ARRAY_SIZE/2; i = i+2)
+    {
+      ADCVoltageSum += AdcOutArray[i];
+      ADCCurrentSum += AdcOutArray[i+1];
+    }
+  } else if (DMA1->ISR & DMA_ISR_TCIF1 ) /* transfer complete */
+  {
+    int i;
+
+    /* transfer complete */
+    DMA1->IFCR |= DMA_IFCR_CGIF1; /* Clear all interrupt flags */
+    DMA1_Channel1->CCR |= DMA_CCR1_HTIE; /* enable Half traisfer interrupt  */
+
+    for (i=ADC_ARRAY_SIZE/2; i< ADC_ARRAY_SIZE; i=i+2)
+    {
+      ADCVoltageSum += AdcOutArray[i];
+      ADCCurrentSum += AdcOutArray[i+1];
+    }
+
+    DMACounter++;
+    if (DMACounter == 16) /* 2Mhz/195/ADC_ARRAY_SIZE*2/16 - refresh frenq = 5 Hz */
+    {
+      DMACounter = 0;
+      ADCVoltage = ADCVoltageSum;
+      ADCCurrent = ADCCurrentSum;
+      NVIC_SetPendingIRQ(EXTI4_IRQn);
+      ADCVoltageSum = 0;
+      ADCCurrentSum = 0;
+      if ( EventQueue == 0 ) /* Lowest priority */
+        EventQueue = EV_KEY_PRESSED|KEY_ADC; 
+    }
+  }
+  DMA1->IFCR |= DMA_IFCR_CGIF1; /* Clear all interrupt flags */
+}
+
+#if defined(__GNUC__)
+void ADC1_IRQHandler(void)
+#else
+void ADC_IRQHandler(void) /*  Watchdog */
+#endif
+{
+  DAC_V = 0; /* OFF output */
+  DAC_I = 0;
+  ADC1->CR2 = 0; /* Adc Off */
+  GPIOB->BRR = GPIO_BRR_BR0|GPIO_BRR_BR1;
+  GPIOB->CRL &= ~(GPIO_CRL_CNF0|GPIO_CRL_CNF1); /* PUSH pull */
+  DAC->CR = 0; /* Dac OFF */
+
+  /* Save current settings */
+  SaveSettings(0);
+  LcdInit(Contrast*3+42);
+  LcdChr(Y_POSITION*3+X_POSITION*0+14+INVERSE, "Power fault" );
+
+  NVIC->ICER[(ADC1_IRQn >> 0x05)] =	(u32)0x01 << (ADC1_IRQn & (u8)0x1F); /* Off ADC interrupt */
+  NVIC->ICPR[(ADC1_IRQn >> 0x05)] =	(u32)0x01 << (ADC1_IRQn & (u8)0x1F); /* Clear pend ADC interrupt */
+
+  { /* Waiting for power off */
+    __IO uint32_t Delay = 5000000;
+    while (Delay--) ;
+  }
+
+  /* Reboot */
+  NVIC_SystemReset();
+}
+
+void LcdBlank(void)
+{
+  LcdInit(42+Contrast*3);
+  LcdClear();
+}
+
+int16_t HumanV;
+int16_t HumanI;
+
+
+void EXTI4_IRQHandler(void)
+{
+  HumanV = VoltageFromAdc(); 
+  HumanI = CurrentFromAdc();
+#if defined(GRAPH)
+  {
+    static Graph_t I = {32000,-32000};
+    static Graph_t V = {32000,-32000};
+    static uint16_t Time;
+
+    if (  HumanI < I.Min )
+      I.Min = HumanI;
+    if ( HumanI > I.Max )
+      I.Max = HumanI;
+    if (HumanV < V.Min)
+      V.Min = HumanV;
+    if (HumanV > V.Max)
+      V.Max = HumanV;
+    Time++;
+    if (Time >= TimeInterval)
+    {
+      Time = 0;
+      GraphCurrentPoint++;
+      if (GraphCurrentPoint == GRAPH_SIZE)
+        GraphCurrentPoint = 0;
+      IGraphArray[GraphCurrentPoint] = I;
+      VGraphArray[GraphCurrentPoint] = V;
+      I.Min = 32000;
+      I.Max = -32000;
+      V.Min = 32000;
+      V.Max = -32000;
+    }
+  }
+#endif
+}
+
+#if 1
+void SystemInit() 
+{};
+
+
+int main()
+{
+ /* init hardware */
+  SCB->VTOR = FLASH_BASE;
+  NVIC_EnableIRQ(DMA1_Channel1_IRQn); /* Enable DMA interrupt */
+  NVIC_EnableIRQ(ADC1_IRQn); /* Enable ADC interrupt */
+  NVIC_EnableIRQ(EXTI4_IRQn); /*Additional IRQ to calculate V and I */
+  NVIC_SetPriority(EXTI4_IRQn, 12); /* Low priority */
+
+  /* CLOCK = 8MHz / 8 = 1 MHz */
+  RCC->CFGR |= RCC_CFGR_HPRE_DIV2|RCC_CFGR_PPRE1_DIV4|RCC_CFGR_PPRE2_DIV2; /* APB1 - 1MHz, AHB - 4MHz, APB2 - 2MHz, ADC - 1MHz */
+  /* Enable peripery clock */
+  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |RCC_APB2ENR_ADC1EN|RCC_APB2ENR_TIM1EN;
+  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN|RCC_APB1ENR_TIM2EN|RCC_APB1ENR_DACEN|RCC_APB1ENR_PWREN|RCC_APB1ENR_BKPEN; 
+  RCC->AHBENR  |= RCC_AHBENR_DMA1EN|RCC_AHBENR_CRCEN; 
+
+  /* Unlock the backup domain */
+  PWR->CR |= PWR_CR_DBP;
+
+  /* GPIO configure */
+  /* PB12 - PB14 - LCD, PA0 - PA2 - encoder(TIM2), PA4-PA5 - DAC Out, PB0-PB1 - ADC(8-9channel), PA7 - ADC power measure */
+  /* PB0 - Voltage IN, PB1 - CurrentIN, PA4 - VoltageOut, PA5 - CurrentOut */
+  GPIOB->CRH |= GPIO_CRH_MODE12_1|GPIO_CRH_MODE13_1|GPIO_CRH_MODE14_1|GPIO_CRH_MODE15_1; /* Output 2MHz */
+  GPIOB->CRH &= ~(GPIO_CRH_CNF12|GPIO_CRH_CNF13|GPIO_CRH_CNF14|GPIO_CRH_CNF15); /* Output Push-pull */
+  GPIOB->CRL |= GPIO_CRL_MODE0_1 |GPIO_CRL_MODE1_1;
+  GPIOB->CRL &= ~(GPIO_CRL_CNF0|GPIO_CRL_CNF1); /* PUSH pull */
+  
+  GPIOA->CRL |= GPIO_CRL_MODE4_1 |GPIO_CRL_MODE5_1|GPIO_CRL_MODE6_1|GPIO_CRL_MODE7_1; /* Out 2 MHZ*/
+  GPIOA->CRL &= ~(GPIO_CRL_CNF4|GPIO_CRL_CNF5|GPIO_CRL_CNF6|GPIO_CRL_CNF7); /* PUSH pull */
+  GPIOA->BSRR =  GPIO_BSRR_BS6;
+
+  LcdBlank();
+  /* Enable the programmable voltage detector */
+  PWR->CR |= PWR_CR_PLS_2|PWR_CR_PLS_1; /* 2.8 V */
+  PWR->CR |= PWR_CR_PVDE; /* Enable the PVD */
+
+  { /* Waiting for power stabilize */
+    __IO uint32_t Delay = 1000000;
+    while (Delay--) ;
+  }
+
+  /* Check power supply */
+  while ( (PWR->CSR & PWR_CSR_PVDO ) != 0 )
+    ; /* BLANK */
+  /* Power is OK for ADC */
+
+  /* Waiting for stabilize power voltage. It should be more then 1.2 V on the PB5 pin */
+  {
+    /* Measure PB5 and Vint by injected group */
+    GPIOA->CRL &= ~GPIO_CRL_MODE7; /* Input, Analog*/
+    ADC1->CR1 |= ADC_CR1_SCAN;
+    ADC1->CR2 |= ADC_CR2_TSVREFE|ADC_CR2_JEXTTRIG | ADC_CR2_JEXTSEL; /* injected start by JSWSTART */
+    ADC1->SMPR1 |= ADC_SMPR1_SMP17; /* Internal Vref, max sampling time */
+    ADC1->SMPR2 |= ADC_SMPR2_SMP7; /* max sampling time */
+    ADC1->CR2 |= ADC_CR2_ADON; /* On the ADC */
+    
+    /* Additional delay to Vref on */
+    Delay(10000);
+
+    /* Calibration */
+    ADC1->CR2 |= ADC_CR2_RSTCAL;
+    while ( ADC1->CR2 & ADC_CR2_RSTCAL )
+      ; /* BLANK */
+    ADC1->CR2 |= ADC_CR2_CAL;
+    while ( ADC1->CR2 & ADC_CR2_CAL )
+      ; /* BLANK */
+
+    Delay(10000);
+  }
+    
+  /* theck the power voltage */
+  /* 2 conversions. 7-th channels, 17-th channel  */
+  ADC1->JSQR |= ADC_JSQR_JL_0| /* 2 conversion */
+                ADC_JSQR_JSQ3_0|ADC_JSQR_JSQ3_1|ADC_JSQR_JSQ3_2| /* CH 7 */
+                ADC_JSQR_JSQ4_4|ADC_JSQR_JSQ4_0; /* CH 17 */
+  do
+  {
+    ADC1->SR &= ~ADC_SR_JEOC; /* Reset convertion flag */
+    ADC1->CR2 |= ADC_CR2_JSWSTART; /* Start the convertion for two channels */
+    while ((ADC1->SR & ADC_SR_JEOC) == 0) /* Waiting end of convertion */
+      ; /* BLANK */
+#if defined(AWD_ENABLED)
+  } while ( ADC1->JDR1/* Vin */ < ADC1->JDR2/* Vref */ );
+#else
+  } while ( 0 );
+#endif
+  {
+    uint16_t SavedVRef = ADC1->JDR2;
+
+    /* Set up analog watch dog */
+    ADC1->CR2 = 0; /* Power off */
+    /* Reset the ADC */
+    RCC->APB2RSTR |= RCC_APB2RSTR_ADC1RST;
+    RCC->APB2RSTR &= ~RCC_APB2RSTR_ADC1RST;
+
+    ADC1->HTR = 0xFFF; /* Max value for HIGHT threshold*/
+    ADC1->LTR = SavedVRef/6*5; /* 1 V */
+  }
+
+  /* Set up ADC, TIM1, DMA for conversion */
+
+  ADC1->CR1 |= 
+#if defined(AWD_ENABLED)
+                ADC_CR1_JAWDEN|ADC_CR1_AWDIE| /* AWD on injected channels whith interrupt */
+#endif
+                ADC_CR1_JAUTO|ADC_CR1_SCAN; /* Scan mode + auto injection */
+  ADC1->SMPR2 = ADC_SMPR2_SMP7_0|ADC_SMPR2_SMP8_0|ADC_SMPR2_SMP9_0; /* 7.5 sampling time, 20 - adc time, 5,6,7 ch */
+  ADC1->SQR1 =  ADC_SQR1_L_0; /* 2 conversion */
+  ADC1->SQR3 =  ADC_SQR3_SQ1_3| /* 8 channel - voltage*/
+                ADC_SQR3_SQ2_3|ADC_SQR3_SQ2_0; /* 9- channel - current */
+  ADC1->JSQR = ADC_JSQR_JSQ4_2|ADC_JSQR_JSQ4_1|ADC_JSQR_JSQ4_0; /* 1 conversion by 7-th channel in inj */
+  ADC1->CR2 |= ADC_CR2_EXTTRIG|ADC_CR2_DMA; /* External trigger by T1 CC1, DMA */
+
+  GPIOA->CRL &= ~(GPIO_CRL_MODE4_1|GPIO_CRL_MODE5_1|GPIO_CRL_MODE7_1); /* Analog IN */
+  GPIOB->CRL &= ~(GPIO_CRL_MODE0_1|GPIO_CRL_MODE1_1); /* Analog IN */
+
+
+  /* TIM1 is configyred to trigger the ADC */
+  TIM1->ARR = 194; /* 2MHz / 200 = 10KHz */
+  TIM1->CCMR1 |= TIM_CCMR1_OC1M; /* PWM 2 mode */
+  TIM1->CCR1 = 99;
+  TIM1->BDTR |= TIM_BDTR_MOE; /* Only for TIM1 - MAIN OUTPUT ENABLE!!! */
+  TIM1->CCER |= TIM_CCER_CC1E; /* Output enable */
+  
+  /* DMA configuring */
+  DMA1_Channel1->CCR |= DMA_CCR1_PL|DMA_CCR1_MSIZE_0|DMA_CCR1_PSIZE_0| /*Hight pry, 16 byte mem, 16 byte pereph */
+          DMA_CCR1_MINC|DMA_CCR1_CIRC|DMA_CCR1_HTIE|DMA_CCR1_TCIE; /*  mem inc, circular, enterrupts by Half and End of conv */
+  DMA1_Channel1->CNDTR = ADC_ARRAY_SIZE;
+  DMA1_Channel1->CPAR = (uint32_t)&ADC1->DR;
+  DMA1_Channel1->CMAR = (uint32_t)&AdcOutArray[0];
+
+  /* On converting */
+  ADC1->CR2 |= ADC_CR2_ADON; /* Adc ON */
+  /* Additional delay to Vref on */
+  Delay(10000);
+
+  /* Calibration */
+  ADC1->CR2 |= ADC_CR2_RSTCAL;
+  while ( ADC1->CR2 & ADC_CR2_RSTCAL )
+    ; /* BLANK */
+  ADC1->CR2 |= ADC_CR2_CAL;
+  while ( ADC1->CR2 & ADC_CR2_CAL )
+    ; /* BLANK */
+
+  Delay(10000);
+  DMA1_Channel1->CCR |= DMA_CCR1_EN; /* Enable DMA */
+  TIM1->CR1 |= TIM_CR1_CEN;    /* start TIM1 */
+
+  /* DAC Init */
+  DAC_V = 0;
+  DAC_I = 0;
+  
+  DAC->CR |= DAC_CR_BOFF2|DAC_CR_BOFF1; /* Disable buffers */
+  DAC->CR |= DAC_CR_EN1|DAC_CR_EN2; /* On DAC */
+
+  LcdBlank();
+  EventInit();
+#if defined(GRAPH)
+  ClearGraph();
+#endif
+
+  if ( RestoreConfig() != 0 || (GPIOA->IDR & KEY_ENTER) == 0 )
+  {
+      AfterContrast = CalibrationMenu;
+      Contrast = 7;
+      CurrentFunc(ContrastMenu);
+//    CurrentFunc(StartFunction);
+  }
+  else
+  {
+    CurrentFunc(StartFunction);
+  }
+#if defined(CLOCK_ENABLED)
+  if ( IS_ON_CLOCK  ) /* RTC clock is on */
+  {
+    SwitchOnTheClock();
+  }
+#endif
+  
+  do
+  	EventCheck();
+  while(1);
+}
+
+#endif

+ 143 - 0
src/LabArm.h

@@ -0,0 +1,143 @@
+#if !defined(__LAB_ARM_H__)
+#define __LAB_ARM_H__
+#include "n3310.h"
+#include "event.h"
+
+#define Delay(tick) \
+{ \
+  __IO int i = tick; \
+  while ( i != 0 ) \
+    i--; \
+}
+
+#define ADC_ARRAY_SIZE 256
+extern uint32_t ADCVoltage;
+extern uint32_t ADCCurrent;
+
+#define AUTO_APPLY_FLAG 0x8000
+#define BACKUP_SAVE_FLAG 0x4000
+#define IMEDDIATE_APPLY_FLAG 0x2000
+extern uint16_t Flags;
+extern uint8_t  Contrast;
+extern MenuFunction_t  AfterContrast;
+extern uint8_t MenuPos;
+void ContrastMenu(void);
+void LcdBlank(void);
+
+
+#define DAC_V (DAC->DHR12R1)
+#define DAC_I (DAC->DHR12R2)
+
+
+void CalibrationMenu(void);
+void StartFunction(void);
+void SaveMenu(void); /* Save settings to flash menu */
+
+#if defined(CLOCK_ENABLED)
+int  SwitchOnTheClock(void);
+void SetupTheClock(void);
+
+/* 
+RTCVal - seconds to display
+InvPos - position that has to be inverted. If the first bit is 1 - display seconds also? else - onlu hours and minutes
+Position - start coordinated to display the clock. It can have size 5 or 8 characters depend on InPos 1-st bit */
+void DisplayClock(uint32_t RTCVal, uint16_t InvPos, uint32_t Position);
+#define IS_ON_CLOCK ((RCC->BDCR & RCC_BDCR_RTCSEL) == RCC_BDCR_RTCSEL_0)
+void TimerSetup(void);
+extern uint16_t TimerValue;
+extern uint16_t RemainTimerValue;
+#endif
+
+typedef struct
+{
+  uint16_t VoltageDAC;  // & Contrast
+  uint16_t CurrentDAC;  // & Flafs
+}Settings_t;
+
+typedef struct
+{
+  float ADCRamp;
+  float ADCOffset;
+  float DACRamp;
+  float DACOffset;
+  int8_t DotPosition;
+  int8_t Res[3];
+}
+SubConfig_t;
+
+typedef struct
+{
+  uint16_t Value;
+  int8_t   DotPosition;
+#define MENU_I_FLAG 1
+#define MENU_V_FLAG 2
+  int8_t   IVFlag;
+  char     Text[4];
+}
+UserMenu_t;
+
+typedef struct
+{
+  SubConfig_t V;
+  SubConfig_t I;
+  UserMenu_t Menu[14];
+  int32_t Crc;
+}Config_t;
+
+#define SAVED_SETTINGS_COUNT (1024 - sizeof(Config_t))/sizeof(Settings_t)
+//#define SAVED_SETTINGS_COUNT 4
+typedef struct 
+{
+  Settings_t   Settings[SAVED_SETTINGS_COUNT];
+  Config_t     Config;
+//  char res[1024-sizeof(Config_t) - SAVED_SETTINGS_COUNT*sizeof(Settings_t)];
+} SavedDomain_t;
+
+void OutValue(uint8_t Y, uint8_t X, uint16_t Num, uint8_t DotPosition, uint8_t SelectPos);
+void OutValueSmall(uint8_t Y, uint8_t X, uint16_t Num, uint8_t DotPosition, uint8_t InverseFlag);
+#if defined(GRAPH)
+#undef RAW
+#endif
+#if defined(RAW)
+extern uint8_t DisplayRaw;
+#endif
+
+//uint32_t Crc(const void* Pointer, int Len);
+void SaveConfig(void);
+int RestoreConfig(void); /* Return !0 if failed */
+void SaveSettings(uint16_t AndFlash);
+
+extern Config_t CurrentConfig;
+extern Settings_t CurrentSettings;
+
+
+int16_t VoltageFromAdc(void);
+int16_t CurrentFromAdc(void);
+int16_t VoltageFromDac(void);
+int16_t CurrentFromDac(void);
+extern int16_t HumanV;
+extern int16_t HumanI;
+
+#if defined(GRAPH)
+#define GRAPH_SIZE 80
+typedef struct
+{
+  int16_t Min;
+  int16_t Max;
+}
+Graph_t;
+extern Graph_t IGraphArray[GRAPH_SIZE];
+extern Graph_t VGraphArray[GRAPH_SIZE];
+extern uint8_t GraphCurrentPoint;
+typedef struct
+{
+  Graph_t* GraphArray;
+  int8_t   DotPosition;
+} GraphData_t;
+extern GraphData_t GraphData;
+extern uint16_t TimeInterval;
+void DisplayGraph(void);
+void ClearGraph(void);
+#endif /*FRAPH*/
+#endif /*__LAB_ARM_H__*/
+

+ 465 - 0
src/LabArm.uvproj

@@ -0,0 +1,465 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<Project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_proj.xsd">
+
+  <SchemaVersion>1.1</SchemaVersion>
+
+  <Header>### uVision Project, (C) Keil Software</Header>
+
+  <Targets>
+    <Target>
+      <TargetName>LabArm</TargetName>
+      <ToolsetNumber>0x4</ToolsetNumber>
+      <ToolsetName>ARM-ADS</ToolsetName>
+      <TargetOption>
+        <TargetCommonOption>
+          <Device>STM32F100C4</Device>
+          <Vendor>STMicroelectronics</Vendor>
+          <Cpu>IRAM(0x20000000-0x20000FFF) IROM(0x8000000-0x8003FFF) CLOCK(8000000) CPUTYPE("Cortex-M3")</Cpu>
+          <FlashUtilSpec></FlashUtilSpec>
+          <StartupFile>"STARTUP\ST\STM32F10x\startup_stm32f10x_ld_vl.s" ("STM32 Low density Value Line Startup Code")</StartupFile>
+          <FlashDriverDll>UL2CM3(-O14 -S0 -C0 -N00("ARM Cortex-M3") -D00(1BA00477) -L00(4) -FO7 -FD20000000 -FC800 -FN1 -FF0STM32F10x_16 -FS08000000 -FL04000)</FlashDriverDll>
+          <DeviceId>5079</DeviceId>
+          <RegisterFile>stm32f10x.h</RegisterFile>
+          <MemoryEnv></MemoryEnv>
+          <Cmp></Cmp>
+          <Asm></Asm>
+          <Linker></Linker>
+          <OHString></OHString>
+          <InfinionOptionDll></InfinionOptionDll>
+          <SLE66CMisc></SLE66CMisc>
+          <SLE66AMisc></SLE66AMisc>
+          <SLE66LinkerMisc></SLE66LinkerMisc>
+          <SFDFile>SFD\ST\STM32F10xx\STM32F10xx4.sfr</SFDFile>
+          <bCustSvd>0</bCustSvd>
+          <UseEnv>0</UseEnv>
+          <BinPath></BinPath>
+          <IncludePath></IncludePath>
+          <LibPath></LibPath>
+          <RegisterFilePath>ST\STM32F10x\</RegisterFilePath>
+          <DBRegisterFilePath>ST\STM32F10x\</DBRegisterFilePath>
+          <TargetStatus>
+            <Error>0</Error>
+            <ExitCodeStop>0</ExitCodeStop>
+            <ButtonStop>0</ButtonStop>
+            <NotGenerated>0</NotGenerated>
+            <InvalidFlash>1</InvalidFlash>
+          </TargetStatus>
+          <OutputDirectory>.\</OutputDirectory>
+          <OutputName>LabArm</OutputName>
+          <CreateExecutable>1</CreateExecutable>
+          <CreateLib>0</CreateLib>
+          <CreateHexFile>1</CreateHexFile>
+          <DebugInformation>1</DebugInformation>
+          <BrowseInformation>1</BrowseInformation>
+          <ListingPath>.\</ListingPath>
+          <HexFormatSelection>1</HexFormatSelection>
+          <Merge32K>0</Merge32K>
+          <CreateBatchFile>0</CreateBatchFile>
+          <BeforeCompile>
+            <RunUserProg1>0</RunUserProg1>
+            <RunUserProg2>0</RunUserProg2>
+            <UserProg1Name></UserProg1Name>
+            <UserProg2Name></UserProg2Name>
+            <UserProg1Dos16Mode>0</UserProg1Dos16Mode>
+            <UserProg2Dos16Mode>0</UserProg2Dos16Mode>
+            <nStopU1X>0</nStopU1X>
+            <nStopU2X>0</nStopU2X>
+          </BeforeCompile>
+          <BeforeMake>
+            <RunUserProg1>0</RunUserProg1>
+            <RunUserProg2>0</RunUserProg2>
+            <UserProg1Name></UserProg1Name>
+            <UserProg2Name></UserProg2Name>
+            <UserProg1Dos16Mode>0</UserProg1Dos16Mode>
+            <UserProg2Dos16Mode>0</UserProg2Dos16Mode>
+            <nStopB1X>0</nStopB1X>
+            <nStopB2X>0</nStopB2X>
+          </BeforeMake>
+          <AfterMake>
+            <RunUserProg1>0</RunUserProg1>
+            <RunUserProg2>0</RunUserProg2>
+            <UserProg1Name></UserProg1Name>
+            <UserProg2Name></UserProg2Name>
+            <UserProg1Dos16Mode>0</UserProg1Dos16Mode>
+            <UserProg2Dos16Mode>0</UserProg2Dos16Mode>
+          </AfterMake>
+          <SelectedForBatchBuild>0</SelectedForBatchBuild>
+          <SVCSIdString></SVCSIdString>
+        </TargetCommonOption>
+        <CommonProperty>
+          <UseCPPCompiler>0</UseCPPCompiler>
+          <RVCTCodeConst>0</RVCTCodeConst>
+          <RVCTZI>0</RVCTZI>
+          <RVCTOtherData>0</RVCTOtherData>
+          <ModuleSelection>0</ModuleSelection>
+          <IncludeInBuild>1</IncludeInBuild>
+          <AlwaysBuild>0</AlwaysBuild>
+          <GenerateAssemblyFile>0</GenerateAssemblyFile>
+          <AssembleAssemblyFile>0</AssembleAssemblyFile>
+          <PublicsOnly>0</PublicsOnly>
+          <StopOnExitCode>3</StopOnExitCode>
+          <CustomArgument></CustomArgument>
+          <IncludeLibraryModules></IncludeLibraryModules>
+          <ComprImg>1</ComprImg>
+        </CommonProperty>
+        <DllOption>
+          <SimDllName>SARMCM3.DLL</SimDllName>
+          <SimDllArguments></SimDllArguments>
+          <SimDlgDll>DCM.DLL</SimDlgDll>
+          <SimDlgDllArguments>-pCM3</SimDlgDllArguments>
+          <TargetDllName>SARMCM3.DLL</TargetDllName>
+          <TargetDllArguments></TargetDllArguments>
+          <TargetDlgDll>TCM.DLL</TargetDlgDll>
+          <TargetDlgDllArguments>-pCM3</TargetDlgDllArguments>
+        </DllOption>
+        <DebugOption>
+          <OPTHX>
+            <HexSelection>1</HexSelection>
+            <HexRangeLowAddress>0</HexRangeLowAddress>
+            <HexRangeHighAddress>0</HexRangeHighAddress>
+            <HexOffset>0</HexOffset>
+            <Oh166RecLen>16</Oh166RecLen>
+          </OPTHX>
+          <Simulator>
+            <UseSimulator>0</UseSimulator>
+            <LoadApplicationAtStartup>1</LoadApplicationAtStartup>
+            <RunToMain>1</RunToMain>
+            <RestoreBreakpoints>1</RestoreBreakpoints>
+            <RestoreWatchpoints>1</RestoreWatchpoints>
+            <RestoreMemoryDisplay>1</RestoreMemoryDisplay>
+            <RestoreFunctions>1</RestoreFunctions>
+            <RestoreToolbox>1</RestoreToolbox>
+            <LimitSpeedToRealTime>0</LimitSpeedToRealTime>
+            <RestoreSysVw>1</RestoreSysVw>
+          </Simulator>
+          <Target>
+            <UseTarget>1</UseTarget>
+            <LoadApplicationAtStartup>1</LoadApplicationAtStartup>
+            <RunToMain>1</RunToMain>
+            <RestoreBreakpoints>1</RestoreBreakpoints>
+            <RestoreWatchpoints>1</RestoreWatchpoints>
+            <RestoreMemoryDisplay>1</RestoreMemoryDisplay>
+            <RestoreFunctions>0</RestoreFunctions>
+            <RestoreToolbox>1</RestoreToolbox>
+            <RestoreTracepoints>0</RestoreTracepoints>
+            <RestoreSysVw>1</RestoreSysVw>
+          </Target>
+          <RunDebugAfterBuild>0</RunDebugAfterBuild>
+          <TargetSelection>-1</TargetSelection>
+          <SimDlls>
+            <CpuDll></CpuDll>
+            <CpuDllArguments></CpuDllArguments>
+            <PeripheralDll></PeripheralDll>
+            <PeripheralDllArguments></PeripheralDllArguments>
+            <InitializationFile></InitializationFile>
+          </SimDlls>
+          <TargetDlls>
+            <CpuDll></CpuDll>
+            <CpuDllArguments></CpuDllArguments>
+            <PeripheralDll></PeripheralDll>
+            <PeripheralDllArguments></PeripheralDllArguments>
+            <InitializationFile></InitializationFile>
+            <Driver>STLink\ST-LINKIII-KEIL.dll</Driver>
+          </TargetDlls>
+        </DebugOption>
+        <Utilities>
+          <Flash1>
+            <UseTargetDll>1</UseTargetDll>
+            <UseExternalTool>0</UseExternalTool>
+            <RunIndependent>0</RunIndependent>
+            <UpdateFlashBeforeDebugging>1</UpdateFlashBeforeDebugging>
+            <Capability>1</Capability>
+            <DriverSelection>4100</DriverSelection>
+          </Flash1>
+          <bUseTDR>0</bUseTDR>
+          <Flash2>STLink\ST-LINKIII-KEIL.dll</Flash2>
+          <Flash3>"" ()</Flash3>
+          <Flash4></Flash4>
+          <pFcarmOut></pFcarmOut>
+          <pFcarmGrp></pFcarmGrp>
+          <pFcArmRoot></pFcArmRoot>
+          <FcArmLst>0</FcArmLst>
+        </Utilities>
+        <TargetArmAds>
+          <ArmAdsMisc>
+            <GenerateListings>0</GenerateListings>
+            <asHll>1</asHll>
+            <asAsm>1</asAsm>
+            <asMacX>1</asMacX>
+            <asSyms>1</asSyms>
+            <asFals>1</asFals>
+            <asDbgD>1</asDbgD>
+            <asForm>1</asForm>
+            <ldLst>0</ldLst>
+            <ldmm>1</ldmm>
+            <ldXref>1</ldXref>
+            <BigEnd>0</BigEnd>
+            <AdsALst>0</AdsALst>
+            <AdsACrf>1</AdsACrf>
+            <AdsANop>0</AdsANop>
+            <AdsANot>0</AdsANot>
+            <AdsLLst>1</AdsLLst>
+            <AdsLmap>1</AdsLmap>
+            <AdsLcgr>1</AdsLcgr>
+            <AdsLsym>1</AdsLsym>
+            <AdsLszi>1</AdsLszi>
+            <AdsLtoi>1</AdsLtoi>
+            <AdsLsun>1</AdsLsun>
+            <AdsLven>1</AdsLven>
+            <AdsLsxf>1</AdsLsxf>
+            <RvctClst>0</RvctClst>
+            <GenPPlst>0</GenPPlst>
+            <AdsCpuType>"Cortex-M3"</AdsCpuType>
+            <RvctDeviceName></RvctDeviceName>
+            <mOS>0</mOS>
+            <uocRom>0</uocRom>
+            <uocRam>0</uocRam>
+            <hadIROM>1</hadIROM>
+            <hadIRAM>1</hadIRAM>
+            <hadXRAM>0</hadXRAM>
+            <uocXRam>0</uocXRam>
+            <RvdsVP>0</RvdsVP>
+            <hadIRAM2>0</hadIRAM2>
+            <hadIROM2>0</hadIROM2>
+            <StupSel>8</StupSel>
+            <useUlib>1</useUlib>
+            <EndSel>0</EndSel>
+            <uLtcg>0</uLtcg>
+            <RoSelD>3</RoSelD>
+            <RwSelD>5</RwSelD>
+            <CodeSel>0</CodeSel>
+            <OptFeed>1</OptFeed>
+            <NoZi1>0</NoZi1>
+            <NoZi2>0</NoZi2>
+            <NoZi3>0</NoZi3>
+            <NoZi4>0</NoZi4>
+            <NoZi5>0</NoZi5>
+            <Ro1Chk>0</Ro1Chk>
+            <Ro2Chk>0</Ro2Chk>
+            <Ro3Chk>0</Ro3Chk>
+            <Ir1Chk>1</Ir1Chk>
+            <Ir2Chk>0</Ir2Chk>
+            <Ra1Chk>0</Ra1Chk>
+            <Ra2Chk>0</Ra2Chk>
+            <Ra3Chk>0</Ra3Chk>
+            <Im1Chk>1</Im1Chk>
+            <Im2Chk>0</Im2Chk>
+            <OnChipMemories>
+              <Ocm1>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm1>
+              <Ocm2>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm2>
+              <Ocm3>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm3>
+              <Ocm4>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm4>
+              <Ocm5>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm5>
+              <Ocm6>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm6>
+              <IRAM>
+                <Type>0</Type>
+                <StartAddress>0x20000000</StartAddress>
+                <Size>0x1000</Size>
+              </IRAM>
+              <IROM>
+                <Type>1</Type>
+                <StartAddress>0x8000000</StartAddress>
+                <Size>0x4000</Size>
+              </IROM>
+              <XRAM>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </XRAM>
+              <OCR_RVCT1>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT1>
+              <OCR_RVCT2>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT2>
+              <OCR_RVCT3>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT3>
+              <OCR_RVCT4>
+                <Type>1</Type>
+                <StartAddress>0x8000000</StartAddress>
+                <Size>0x4000</Size>
+              </OCR_RVCT4>
+              <OCR_RVCT5>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT5>
+              <OCR_RVCT6>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT6>
+              <OCR_RVCT7>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT7>
+              <OCR_RVCT8>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT8>
+              <OCR_RVCT9>
+                <Type>0</Type>
+                <StartAddress>0x20000000</StartAddress>
+                <Size>0x1000</Size>
+              </OCR_RVCT9>
+              <OCR_RVCT10>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT10>
+            </OnChipMemories>
+            <RvctStartVector></RvctStartVector>
+          </ArmAdsMisc>
+          <Cads>
+            <interw>1</interw>
+            <Optim>4</Optim>
+            <oTime>0</oTime>
+            <SplitLS>0</SplitLS>
+            <OneElfS>0</OneElfS>
+            <Strict>0</Strict>
+            <EnumInt>0</EnumInt>
+            <PlainCh>0</PlainCh>
+            <Ropi>0</Ropi>
+            <Rwpi>0</Rwpi>
+            <wLevel>0</wLevel>
+            <uThumb>0</uThumb>
+            <uSurpInc>0</uSurpInc>
+            <uC99>0</uC99>
+            <useXO>0</useXO>
+            <VariousControls>
+              <MiscControls></MiscControls>
+              <Define>STM32F10X_LD_VL BIG REAL_FLASH AWD_ENABLED CLOCK_ENABLED GRAPH</Define>
+              <Undefine></Undefine>
+              <IncludePath></IncludePath>
+            </VariousControls>
+          </Cads>
+          <Aads>
+            <interw>1</interw>
+            <Ropi>0</Ropi>
+            <Rwpi>0</Rwpi>
+            <thumb>0</thumb>
+            <SplitLS>0</SplitLS>
+            <SwStkChk>0</SwStkChk>
+            <NoWarn>0</NoWarn>
+            <uSurpInc>0</uSurpInc>
+            <useXO>0</useXO>
+            <VariousControls>
+              <MiscControls></MiscControls>
+              <Define></Define>
+              <Undefine></Undefine>
+              <IncludePath></IncludePath>
+            </VariousControls>
+          </Aads>
+          <LDads>
+            <umfTarg>1</umfTarg>
+            <Ropi>0</Ropi>
+            <Rwpi>0</Rwpi>
+            <noStLib>0</noStLib>
+            <RepFail>1</RepFail>
+            <useFile>0</useFile>
+            <TextAddressRange>0x08000000</TextAddressRange>
+            <DataAddressRange>0x20000000</DataAddressRange>
+            <pXoBase></pXoBase>
+            <ScatterFile></ScatterFile>
+            <IncludeLibs></IncludeLibs>
+            <IncludeLibsPath></IncludeLibsPath>
+            <Misc></Misc>
+            <LinkerInputFile></LinkerInputFile>
+            <DisabledWarnings></DisabledWarnings>
+          </LDads>
+        </TargetArmAds>
+      </TargetOption>
+      <Groups>
+        <Group>
+          <GroupName>asm</GroupName>
+          <Files>
+            <File>
+              <FileName>STM32F10x.s</FileName>
+              <FileType>2</FileType>
+              <FilePath>.\STM32F10x.s</FilePath>
+            </File>
+          </Files>
+        </Group>
+        <Group>
+          <GroupName>Src</GroupName>
+          <Files>
+            <File>
+              <FileName>n3310.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\n3310.c</FilePath>
+            </File>
+            <File>
+              <FileName>event.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\event.c</FilePath>
+            </File>
+            <File>
+              <FileName>LabArm.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\LabArm.c</FilePath>
+            </File>
+            <File>
+              <FileName>config.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\config.c</FilePath>
+            </File>
+            <File>
+              <FileName>ui.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\ui.c</FilePath>
+            </File>
+            <File>
+              <FileName>calibration.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\calibration.c</FilePath>
+            </File>
+            <File>
+              <FileName>clock.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\clock.c</FilePath>
+            </File>
+            <File>
+              <FileName>graph.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\graph.c</FilePath>
+            </File>
+          </Files>
+        </Group>
+      </Groups>
+    </Target>
+  </Targets>
+
+</Project>

+ 483 - 0
src/LabArm.uvprojx

@@ -0,0 +1,483 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
+<Project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="project_projx.xsd">
+
+  <SchemaVersion>2.1</SchemaVersion>
+
+  <Header>### uVision Project, (C) Keil Software</Header>
+
+  <Targets>
+    <Target>
+      <TargetName>LabArm</TargetName>
+      <ToolsetNumber>0x4</ToolsetNumber>
+      <ToolsetName>ARM-ADS</ToolsetName>
+      <TargetOption>
+        <TargetCommonOption>
+          <Device>STM32F100C4</Device>
+          <Vendor>STMicroelectronics</Vendor>
+          <PackID>Keil.STM32F1xx_DFP.1.0.5</PackID>
+          <PackURL>http://www.keil.com/pack/</PackURL>
+          <Cpu>IROM(0x08000000,0x4000) IRAM(0x20000000,0x1000) CPUTYPE("Cortex-M3") CLOCK(12000000) ELITTLE</Cpu>
+          <FlashUtilSpec></FlashUtilSpec>
+          <StartupFile></StartupFile>
+          <FlashDriverDll>UL2CM3(-S0 -C0 -P0 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_16 -FS08000000 -FL04000 -FP0($$Device:STM32F100C4$Flash\STM32F10x_16.FLM))</FlashDriverDll>
+          <DeviceId>0</DeviceId>
+          <RegisterFile>$$Device:STM32F100C4$Device\Include\stm32f10x.h</RegisterFile>
+          <MemoryEnv></MemoryEnv>
+          <Cmp></Cmp>
+          <Asm></Asm>
+          <Linker></Linker>
+          <OHString></OHString>
+          <InfinionOptionDll></InfinionOptionDll>
+          <SLE66CMisc></SLE66CMisc>
+          <SLE66AMisc></SLE66AMisc>
+          <SLE66LinkerMisc></SLE66LinkerMisc>
+          <SFDFile>$$Device:STM32F100C4$SVD\STM32F100xx.svd</SFDFile>
+          <bCustSvd>0</bCustSvd>
+          <UseEnv>0</UseEnv>
+          <BinPath></BinPath>
+          <IncludePath></IncludePath>
+          <LibPath></LibPath>
+          <RegisterFilePath></RegisterFilePath>
+          <DBRegisterFilePath></DBRegisterFilePath>
+          <TargetStatus>
+            <Error>0</Error>
+            <ExitCodeStop>0</ExitCodeStop>
+            <ButtonStop>0</ButtonStop>
+            <NotGenerated>0</NotGenerated>
+            <InvalidFlash>1</InvalidFlash>
+          </TargetStatus>
+          <OutputDirectory>.\</OutputDirectory>
+          <OutputName>LabArm</OutputName>
+          <CreateExecutable>1</CreateExecutable>
+          <CreateLib>0</CreateLib>
+          <CreateHexFile>1</CreateHexFile>
+          <DebugInformation>1</DebugInformation>
+          <BrowseInformation>1</BrowseInformation>
+          <ListingPath>.\</ListingPath>
+          <HexFormatSelection>1</HexFormatSelection>
+          <Merge32K>0</Merge32K>
+          <CreateBatchFile>0</CreateBatchFile>
+          <BeforeCompile>
+            <RunUserProg1>0</RunUserProg1>
+            <RunUserProg2>0</RunUserProg2>
+            <UserProg1Name></UserProg1Name>
+            <UserProg2Name></UserProg2Name>
+            <UserProg1Dos16Mode>0</UserProg1Dos16Mode>
+            <UserProg2Dos16Mode>0</UserProg2Dos16Mode>
+            <nStopU1X>0</nStopU1X>
+            <nStopU2X>0</nStopU2X>
+          </BeforeCompile>
+          <BeforeMake>
+            <RunUserProg1>0</RunUserProg1>
+            <RunUserProg2>0</RunUserProg2>
+            <UserProg1Name></UserProg1Name>
+            <UserProg2Name></UserProg2Name>
+            <UserProg1Dos16Mode>0</UserProg1Dos16Mode>
+            <UserProg2Dos16Mode>0</UserProg2Dos16Mode>
+            <nStopB1X>0</nStopB1X>
+            <nStopB2X>0</nStopB2X>
+          </BeforeMake>
+          <AfterMake>
+            <RunUserProg1>0</RunUserProg1>
+            <RunUserProg2>0</RunUserProg2>
+            <UserProg1Name></UserProg1Name>
+            <UserProg2Name></UserProg2Name>
+            <UserProg1Dos16Mode>0</UserProg1Dos16Mode>
+            <UserProg2Dos16Mode>0</UserProg2Dos16Mode>
+          </AfterMake>
+          <SelectedForBatchBuild>0</SelectedForBatchBuild>
+          <SVCSIdString></SVCSIdString>
+        </TargetCommonOption>
+        <CommonProperty>
+          <UseCPPCompiler>0</UseCPPCompiler>
+          <RVCTCodeConst>0</RVCTCodeConst>
+          <RVCTZI>0</RVCTZI>
+          <RVCTOtherData>0</RVCTOtherData>
+          <ModuleSelection>0</ModuleSelection>
+          <IncludeInBuild>1</IncludeInBuild>
+          <AlwaysBuild>0</AlwaysBuild>
+          <GenerateAssemblyFile>0</GenerateAssemblyFile>
+          <AssembleAssemblyFile>0</AssembleAssemblyFile>
+          <PublicsOnly>0</PublicsOnly>
+          <StopOnExitCode>3</StopOnExitCode>
+          <CustomArgument></CustomArgument>
+          <IncludeLibraryModules></IncludeLibraryModules>
+          <ComprImg>1</ComprImg>
+        </CommonProperty>
+        <DllOption>
+          <SimDllName>SARMCM3.DLL</SimDllName>
+          <SimDllArguments> -REMAP</SimDllArguments>
+          <SimDlgDll>DCM.DLL</SimDlgDll>
+          <SimDlgDllArguments>-pCM3</SimDlgDllArguments>
+          <TargetDllName>SARMCM3.DLL</TargetDllName>
+          <TargetDllArguments></TargetDllArguments>
+          <TargetDlgDll>TCM.DLL</TargetDlgDll>
+          <TargetDlgDllArguments>-pCM3</TargetDlgDllArguments>
+        </DllOption>
+        <DebugOption>
+          <OPTHX>
+            <HexSelection>1</HexSelection>
+            <HexRangeLowAddress>0</HexRangeLowAddress>
+            <HexRangeHighAddress>0</HexRangeHighAddress>
+            <HexOffset>0</HexOffset>
+            <Oh166RecLen>16</Oh166RecLen>
+          </OPTHX>
+          <Simulator>
+            <UseSimulator>0</UseSimulator>
+            <LoadApplicationAtStartup>1</LoadApplicationAtStartup>
+            <RunToMain>1</RunToMain>
+            <RestoreBreakpoints>1</RestoreBreakpoints>
+            <RestoreWatchpoints>1</RestoreWatchpoints>
+            <RestoreMemoryDisplay>1</RestoreMemoryDisplay>
+            <RestoreFunctions>1</RestoreFunctions>
+            <RestoreToolbox>1</RestoreToolbox>
+            <LimitSpeedToRealTime>0</LimitSpeedToRealTime>
+            <RestoreSysVw>1</RestoreSysVw>
+          </Simulator>
+          <Target>
+            <UseTarget>1</UseTarget>
+            <LoadApplicationAtStartup>1</LoadApplicationAtStartup>
+            <RunToMain>1</RunToMain>
+            <RestoreBreakpoints>1</RestoreBreakpoints>
+            <RestoreWatchpoints>1</RestoreWatchpoints>
+            <RestoreMemoryDisplay>1</RestoreMemoryDisplay>
+            <RestoreFunctions>0</RestoreFunctions>
+            <RestoreToolbox>1</RestoreToolbox>
+            <RestoreTracepoints>0</RestoreTracepoints>
+            <RestoreSysVw>1</RestoreSysVw>
+          </Target>
+          <RunDebugAfterBuild>0</RunDebugAfterBuild>
+          <TargetSelection>1</TargetSelection>
+          <SimDlls>
+            <CpuDll></CpuDll>
+            <CpuDllArguments></CpuDllArguments>
+            <PeripheralDll></PeripheralDll>
+            <PeripheralDllArguments></PeripheralDllArguments>
+            <InitializationFile></InitializationFile>
+          </SimDlls>
+          <TargetDlls>
+            <CpuDll></CpuDll>
+            <CpuDllArguments></CpuDllArguments>
+            <PeripheralDll></PeripheralDll>
+            <PeripheralDllArguments></PeripheralDllArguments>
+            <InitializationFile></InitializationFile>
+            <Driver>BIN\UL2CM3.DLL</Driver>
+          </TargetDlls>
+        </DebugOption>
+        <Utilities>
+          <Flash1>
+            <UseTargetDll>1</UseTargetDll>
+            <UseExternalTool>0</UseExternalTool>
+            <RunIndependent>0</RunIndependent>
+            <UpdateFlashBeforeDebugging>1</UpdateFlashBeforeDebugging>
+            <Capability>1</Capability>
+            <DriverSelection>4100</DriverSelection>
+          </Flash1>
+          <bUseTDR>1</bUseTDR>
+          <Flash2>BIN\UL2CM3.DLL</Flash2>
+          <Flash3></Flash3>
+          <Flash4></Flash4>
+          <pFcarmOut></pFcarmOut>
+          <pFcarmGrp></pFcarmGrp>
+          <pFcArmRoot></pFcArmRoot>
+          <FcArmLst>0</FcArmLst>
+        </Utilities>
+        <TargetArmAds>
+          <ArmAdsMisc>
+            <GenerateListings>0</GenerateListings>
+            <asHll>1</asHll>
+            <asAsm>1</asAsm>
+            <asMacX>1</asMacX>
+            <asSyms>1</asSyms>
+            <asFals>1</asFals>
+            <asDbgD>1</asDbgD>
+            <asForm>1</asForm>
+            <ldLst>0</ldLst>
+            <ldmm>1</ldmm>
+            <ldXref>1</ldXref>
+            <BigEnd>0</BigEnd>
+            <AdsALst>0</AdsALst>
+            <AdsACrf>1</AdsACrf>
+            <AdsANop>0</AdsANop>
+            <AdsANot>0</AdsANot>
+            <AdsLLst>1</AdsLLst>
+            <AdsLmap>1</AdsLmap>
+            <AdsLcgr>1</AdsLcgr>
+            <AdsLsym>1</AdsLsym>
+            <AdsLszi>1</AdsLszi>
+            <AdsLtoi>1</AdsLtoi>
+            <AdsLsun>1</AdsLsun>
+            <AdsLven>1</AdsLven>
+            <AdsLsxf>1</AdsLsxf>
+            <RvctClst>0</RvctClst>
+            <GenPPlst>0</GenPPlst>
+            <AdsCpuType>"Cortex-M3"</AdsCpuType>
+            <RvctDeviceName></RvctDeviceName>
+            <mOS>0</mOS>
+            <uocRom>0</uocRom>
+            <uocRam>0</uocRam>
+            <hadIROM>1</hadIROM>
+            <hadIRAM>1</hadIRAM>
+            <hadXRAM>0</hadXRAM>
+            <uocXRam>0</uocXRam>
+            <RvdsVP>0</RvdsVP>
+            <hadIRAM2>0</hadIRAM2>
+            <hadIROM2>0</hadIROM2>
+            <StupSel>8</StupSel>
+            <useUlib>1</useUlib>
+            <EndSel>0</EndSel>
+            <uLtcg>0</uLtcg>
+            <RoSelD>3</RoSelD>
+            <RwSelD>3</RwSelD>
+            <CodeSel>0</CodeSel>
+            <OptFeed>1</OptFeed>
+            <NoZi1>0</NoZi1>
+            <NoZi2>0</NoZi2>
+            <NoZi3>0</NoZi3>
+            <NoZi4>0</NoZi4>
+            <NoZi5>0</NoZi5>
+            <Ro1Chk>0</Ro1Chk>
+            <Ro2Chk>0</Ro2Chk>
+            <Ro3Chk>0</Ro3Chk>
+            <Ir1Chk>1</Ir1Chk>
+            <Ir2Chk>0</Ir2Chk>
+            <Ra1Chk>0</Ra1Chk>
+            <Ra2Chk>0</Ra2Chk>
+            <Ra3Chk>0</Ra3Chk>
+            <Im1Chk>1</Im1Chk>
+            <Im2Chk>0</Im2Chk>
+            <OnChipMemories>
+              <Ocm1>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm1>
+              <Ocm2>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm2>
+              <Ocm3>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm3>
+              <Ocm4>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm4>
+              <Ocm5>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm5>
+              <Ocm6>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </Ocm6>
+              <IRAM>
+                <Type>0</Type>
+                <StartAddress>0x20000000</StartAddress>
+                <Size>0x1000</Size>
+              </IRAM>
+              <IROM>
+                <Type>1</Type>
+                <StartAddress>0x8000000</StartAddress>
+                <Size>0x4000</Size>
+              </IROM>
+              <XRAM>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </XRAM>
+              <OCR_RVCT1>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT1>
+              <OCR_RVCT2>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT2>
+              <OCR_RVCT3>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT3>
+              <OCR_RVCT4>
+                <Type>1</Type>
+                <StartAddress>0x8000000</StartAddress>
+                <Size>0x4000</Size>
+              </OCR_RVCT4>
+              <OCR_RVCT5>
+                <Type>1</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT5>
+              <OCR_RVCT6>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT6>
+              <OCR_RVCT7>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT7>
+              <OCR_RVCT8>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT8>
+              <OCR_RVCT9>
+                <Type>0</Type>
+                <StartAddress>0x20000000</StartAddress>
+                <Size>0x1000</Size>
+              </OCR_RVCT9>
+              <OCR_RVCT10>
+                <Type>0</Type>
+                <StartAddress>0x0</StartAddress>
+                <Size>0x0</Size>
+              </OCR_RVCT10>
+            </OnChipMemories>
+            <RvctStartVector></RvctStartVector>
+          </ArmAdsMisc>
+          <Cads>
+            <interw>1</interw>
+            <Optim>4</Optim>
+            <oTime>0</oTime>
+            <SplitLS>0</SplitLS>
+            <OneElfS>0</OneElfS>
+            <Strict>0</Strict>
+            <EnumInt>0</EnumInt>
+            <PlainCh>0</PlainCh>
+            <Ropi>0</Ropi>
+            <Rwpi>0</Rwpi>
+            <wLevel>0</wLevel>
+            <uThumb>0</uThumb>
+            <uSurpInc>0</uSurpInc>
+            <uC99>0</uC99>
+            <useXO>0</useXO>
+            <VariousControls>
+              <MiscControls></MiscControls>
+              <Define>STM32F10X_LD_VL BIG REAL_FLASH AWD_ENABLED CLOCK_ENABLED GRAPH</Define>
+              <Undefine></Undefine>
+              <IncludePath></IncludePath>
+            </VariousControls>
+          </Cads>
+          <Aads>
+            <interw>1</interw>
+            <Ropi>0</Ropi>
+            <Rwpi>0</Rwpi>
+            <thumb>0</thumb>
+            <SplitLS>0</SplitLS>
+            <SwStkChk>0</SwStkChk>
+            <NoWarn>0</NoWarn>
+            <uSurpInc>0</uSurpInc>
+            <useXO>0</useXO>
+            <VariousControls>
+              <MiscControls></MiscControls>
+              <Define></Define>
+              <Undefine></Undefine>
+              <IncludePath></IncludePath>
+            </VariousControls>
+          </Aads>
+          <LDads>
+            <umfTarg>1</umfTarg>
+            <Ropi>0</Ropi>
+            <Rwpi>0</Rwpi>
+            <noStLib>0</noStLib>
+            <RepFail>1</RepFail>
+            <useFile>0</useFile>
+            <TextAddressRange>0x08000000</TextAddressRange>
+            <DataAddressRange>0x20000000</DataAddressRange>
+            <pXoBase></pXoBase>
+            <ScatterFile></ScatterFile>
+            <IncludeLibs></IncludeLibs>
+            <IncludeLibsPath></IncludeLibsPath>
+            <Misc></Misc>
+            <LinkerInputFile></LinkerInputFile>
+            <DisabledWarnings></DisabledWarnings>
+          </LDads>
+        </TargetArmAds>
+      </TargetOption>
+      <Groups>
+        <Group>
+          <GroupName>asm</GroupName>
+          <Files>
+            <File>
+              <FileName>STM32F10x.s</FileName>
+              <FileType>2</FileType>
+              <FilePath>.\STM32F10x.s</FilePath>
+            </File>
+          </Files>
+        </Group>
+        <Group>
+          <GroupName>Src</GroupName>
+          <Files>
+            <File>
+              <FileName>n3310.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\n3310.c</FilePath>
+            </File>
+            <File>
+              <FileName>event.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\event.c</FilePath>
+            </File>
+            <File>
+              <FileName>LabArm.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\LabArm.c</FilePath>
+            </File>
+            <File>
+              <FileName>config.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\config.c</FilePath>
+            </File>
+            <File>
+              <FileName>ui.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\ui.c</FilePath>
+            </File>
+            <File>
+              <FileName>calibration.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\calibration.c</FilePath>
+            </File>
+            <File>
+              <FileName>clock.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\clock.c</FilePath>
+            </File>
+            <File>
+              <FileName>graph.c</FileName>
+              <FileType>1</FileType>
+              <FilePath>.\graph.c</FilePath>
+            </File>
+          </Files>
+        </Group>
+        <Group>
+          <GroupName>::CMSIS</GroupName>
+        </Group>
+      </Groups>
+    </Target>
+  </Targets>
+
+  <RTE>
+    <apis/>
+    <components>
+      <component Cclass="CMSIS" Cgroup="CORE" Cvendor="ARM" Cversion="3.40.0" condition="CMSIS Core">
+        <package name="CMSIS" schemaVersion="1.3" url="http://www.keil.com/pack/" vendor="ARM" version="4.2.0"/>
+        <targetInfos>
+          <targetInfo name="LabArm"/>
+        </targetInfos>
+      </component>
+    </components>
+    <files/>
+  </RTE>
+
+</Project>

+ 289 - 0
src/STM32F10x.s

@@ -0,0 +1,289 @@
+;/*****************************************************************************/
+;/* STM32F10x.s: Startup file for ST STM32F10x device series                  */
+;/*****************************************************************************/
+;/* <<< Use Configuration Wizard in Context Menu >>>                          */
+;/*****************************************************************************/
+;/* This file is part of the uVision/ARM development tools.                   */
+;/* Copyright (c) 2005-2007 Keil Software. All rights reserved.               */
+;/* This software may only be used under the terms of a valid, current,       */
+;/* end user licence from KEIL for a compatible version of KEIL software      */
+;/* development tools. Nothing else gives you the right to use this software. */
+;/*****************************************************************************/
+
+
+;// <h> Stack Configuration
+;//   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
+;// </h>
+
+Stack_Size      EQU     0x00000200
+
+                AREA    STACK, NOINIT, READWRITE, ALIGN=3
+Stack_Mem       SPACE   Stack_Size
+__initial_sp
+
+
+;// <h> Heap Configuration
+;//   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
+;// </h>
+
+Heap_Size       EQU     0x00000000
+
+                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
+__heap_base
+Heap_Mem        SPACE   Heap_Size
+__heap_limit
+
+
+                PRESERVE8
+                THUMB
+
+
+; Vector Table Mapped to Address 0 at Reset
+
+                AREA    RESET, DATA, READONLY
+                EXPORT  __Vectors
+
+__Vectors       DCD     __initial_sp              ; Top of Stack
+                DCD     Reset_Handler             ; Reset Handler
+                DCD     NMI_Handler               ; NMI Handler
+                DCD     HardFault_Handler         ; Hard Fault Handler
+                DCD     MemManage_Handler         ; MPU Fault Handler
+                DCD     BusFault_Handler          ; Bus Fault Handler
+                DCD     UsageFault_Handler        ; Usage Fault Handler
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     0                         ; Reserved
+                DCD     SVC_Handler               ; SVCall Handler
+                DCD     DebugMon_Handler          ; Debug Monitor Handler
+                DCD     0                         ; Reserved
+                DCD     PendSV_Handler            ; PendSV Handler
+                DCD     SysTick_Handler           ; SysTick Handler
+
+                ; External Interrupts
+                DCD     WWDG_IRQHandler           ; Window Watchdog
+                DCD     PVD_IRQHandler            ; PVD through EXTI Line detect
+                DCD     TAMPER_IRQHandler         ; Tamper
+                DCD     RTC_IRQHandler            ; RTC
+                DCD     FLASH_IRQHandler          ; Flash
+                DCD     RCC_IRQHandler            ; RCC
+                DCD     EXTI0_IRQHandler          ; EXTI Line 0
+                DCD     EXTI1_IRQHandler          ; EXTI Line 1
+                DCD     EXTI2_IRQHandler          ; EXTI Line 2
+                DCD     EXTI3_IRQHandler          ; EXTI Line 3
+                DCD     EXTI4_IRQHandler          ; EXTI Line 4
+                DCD     DMAChannel1_IRQHandler    ; DMA Channel 1
+                DCD     DMAChannel2_IRQHandler    ; DMA Channel 2
+                DCD     DMAChannel3_IRQHandler    ; DMA Channel 3
+                DCD     DMAChannel4_IRQHandler    ; DMA Channel 4
+                DCD     DMAChannel5_IRQHandler    ; DMA Channel 5
+                DCD     DMAChannel6_IRQHandler    ; DMA Channel 6
+                DCD     DMAChannel7_IRQHandler    ; DMA Channel 7
+                DCD     ADC_IRQHandler            ; ADC
+                DCD     USB_HP_CAN_TX_IRQHandler  ; USB High Priority or CAN TX
+                DCD     USB_LP_CAN_RX0_IRQHandler ; USB Low  Priority or CAN RX0
+                DCD     CAN_RX1_IRQHandler        ; CAN RX1
+                DCD     CAN_SCE_IRQHandler        ; CAN SCE
+                DCD     EXTI9_5_IRQHandler        ; EXTI Line 9..5
+                DCD     TIM1_BRK_IRQHandler       ; TIM1 Break
+                DCD     TIM1_UP_IRQHandler        ; TIM1 Update
+                DCD     TIM1_TRG_COM_IRQHandler   ; TIM1 Trigger and Commutation
+                DCD     TIM1_CC_IRQHandler        ; TIM1 Capture Compare
+                DCD     TIM2_IRQHandler           ; TIM2
+                DCD     TIM3_IRQHandler           ; TIM3
+                DCD     TIM4_IRQHandler           ; TIM4
+                DCD     I2C1_EV_IRQHandler        ; I2C1 Event
+                DCD     I2C1_ER_IRQHandler        ; I2C1 Error
+                DCD     I2C2_EV_IRQHandler        ; I2C2 Event
+                DCD     I2C2_ER_IRQHandler        ; I2C2 Error
+                DCD     SPI1_IRQHandler           ; SPI1
+                DCD     SPI2_IRQHandler           ; SPI2
+                DCD     USART1_IRQHandler         ; USART1
+                DCD     USART2_IRQHandler         ; USART2
+                DCD     USART3_IRQHandler         ; USART3
+                DCD     EXTI15_10_IRQHandler      ; EXTI Line 15..10
+                DCD     RTCAlarm_IRQHandler       ; RTC Alarm through EXTI Line
+                DCD     USBWakeUp_IRQHandler      ; USB Wakeup from suspend
+
+
+                AREA    |.text|, CODE, READONLY
+
+
+; Reset Handler
+
+Reset_Handler   PROC
+                EXPORT  Reset_Handler             [WEAK]
+                IMPORT  __main
+                LDR     R0, =__main
+                BX      R0
+                ENDP
+
+
+; Dummy Exception Handlers (infinite loops which can be modified)                
+
+NMI_Handler     PROC
+                EXPORT  NMI_Handler               [WEAK]
+                B       .
+                ENDP
+HardFault_Handler\
+                PROC
+                EXPORT  HardFault_Handler         [WEAK]
+                B       .
+                ENDP
+MemManage_Handler\
+                PROC
+                EXPORT  MemManage_Handler         [WEAK]
+                B       .
+                ENDP
+BusFault_Handler\
+                PROC
+                EXPORT  BusFault_Handler          [WEAK]
+                B       .
+                ENDP
+UsageFault_Handler\
+                PROC
+                EXPORT  UsageFault_Handler        [WEAK]
+                B       .
+                ENDP
+SVC_Handler     PROC
+                EXPORT  SVC_Handler               [WEAK]
+                B       .
+                ENDP
+DebugMon_Handler\
+                PROC
+                EXPORT  DebugMon_Handler          [WEAK]
+                B       .
+                ENDP
+PendSV_Handler  PROC
+                EXPORT  PendSV_Handler            [WEAK]
+                B       .
+                ENDP
+SysTick_Handler PROC
+                EXPORT  SysTick_Handler           [WEAK]
+                B       .
+                ENDP
+
+Default_Handler PROC
+
+                EXPORT  WWDG_IRQHandler           [WEAK]
+                EXPORT  PVD_IRQHandler            [WEAK]
+                EXPORT  TAMPER_IRQHandler         [WEAK]
+                EXPORT  RTC_IRQHandler            [WEAK]
+                EXPORT  FLASH_IRQHandler          [WEAK]
+                EXPORT  RCC_IRQHandler            [WEAK]
+                EXPORT  EXTI0_IRQHandler          [WEAK]
+                EXPORT  EXTI1_IRQHandler          [WEAK]
+                EXPORT  EXTI2_IRQHandler          [WEAK]
+                EXPORT  EXTI3_IRQHandler          [WEAK]
+                EXPORT  EXTI4_IRQHandler          [WEAK]
+                EXPORT  DMAChannel1_IRQHandler    [WEAK]
+                EXPORT  DMAChannel2_IRQHandler    [WEAK]
+                EXPORT  DMAChannel3_IRQHandler    [WEAK]
+                EXPORT  DMAChannel4_IRQHandler    [WEAK]
+                EXPORT  DMAChannel5_IRQHandler    [WEAK]
+                EXPORT  DMAChannel6_IRQHandler    [WEAK]
+                EXPORT  DMAChannel7_IRQHandler    [WEAK]
+                EXPORT  ADC_IRQHandler            [WEAK]
+                EXPORT  USB_HP_CAN_TX_IRQHandler  [WEAK]
+                EXPORT  USB_LP_CAN_RX0_IRQHandler [WEAK]
+                EXPORT  CAN_RX1_IRQHandler        [WEAK]
+                EXPORT  CAN_SCE_IRQHandler        [WEAK]
+                EXPORT  EXTI9_5_IRQHandler        [WEAK]
+                EXPORT  TIM1_BRK_IRQHandler       [WEAK]
+                EXPORT  TIM1_UP_IRQHandler        [WEAK]
+                EXPORT  TIM1_TRG_COM_IRQHandler   [WEAK]
+                EXPORT  TIM1_CC_IRQHandler        [WEAK]
+                EXPORT  TIM2_IRQHandler           [WEAK]
+                EXPORT  TIM3_IRQHandler           [WEAK]
+                EXPORT  TIM4_IRQHandler           [WEAK]
+                EXPORT  I2C1_EV_IRQHandler        [WEAK]
+                EXPORT  I2C1_ER_IRQHandler        [WEAK]
+                EXPORT  I2C2_EV_IRQHandler        [WEAK]
+                EXPORT  I2C2_ER_IRQHandler        [WEAK]
+                EXPORT  SPI1_IRQHandler           [WEAK]
+                EXPORT  SPI2_IRQHandler           [WEAK]
+                EXPORT  USART1_IRQHandler         [WEAK]
+                EXPORT  USART2_IRQHandler         [WEAK]
+                EXPORT  USART3_IRQHandler         [WEAK]
+                EXPORT  EXTI15_10_IRQHandler      [WEAK]
+                EXPORT  RTCAlarm_IRQHandler       [WEAK]
+                EXPORT  USBWakeUp_IRQHandler      [WEAK]
+
+WWDG_IRQHandler
+PVD_IRQHandler
+TAMPER_IRQHandler
+RTC_IRQHandler
+FLASH_IRQHandler
+RCC_IRQHandler
+EXTI0_IRQHandler
+EXTI1_IRQHandler
+EXTI2_IRQHandler
+EXTI3_IRQHandler
+EXTI4_IRQHandler
+DMAChannel1_IRQHandler
+DMAChannel2_IRQHandler
+DMAChannel3_IRQHandler
+DMAChannel4_IRQHandler
+DMAChannel5_IRQHandler
+DMAChannel6_IRQHandler
+DMAChannel7_IRQHandler
+ADC_IRQHandler
+USB_HP_CAN_TX_IRQHandler
+USB_LP_CAN_RX0_IRQHandler
+CAN_RX1_IRQHandler
+CAN_SCE_IRQHandler
+EXTI9_5_IRQHandler
+TIM1_BRK_IRQHandler
+TIM1_UP_IRQHandler
+TIM1_TRG_COM_IRQHandler
+TIM1_CC_IRQHandler
+TIM2_IRQHandler
+TIM3_IRQHandler
+TIM4_IRQHandler
+I2C1_EV_IRQHandler
+I2C1_ER_IRQHandler
+I2C2_EV_IRQHandler
+I2C2_ER_IRQHandler
+SPI1_IRQHandler
+SPI2_IRQHandler
+USART1_IRQHandler
+USART2_IRQHandler
+USART3_IRQHandler
+EXTI15_10_IRQHandler
+RTCAlarm_IRQHandler
+USBWakeUp_IRQHandler
+
+                B       .
+
+                ENDP
+
+
+                ALIGN
+
+
+; User Initial Stack & Heap
+
+                IF      :DEF:__MICROLIB
+                
+                EXPORT  __initial_sp
+                EXPORT  __heap_base
+                EXPORT  __heap_limit
+                
+                ELSE
+                
+                IMPORT  __use_two_region_memory
+                EXPORT  __user_initial_stackheap
+__user_initial_stackheap
+
+                LDR     R0, =  Heap_Mem
+                LDR     R1, =(Stack_Mem + Stack_Size)
+                LDR     R2, = (Heap_Mem +  Heap_Size)
+                LDR     R3, = Stack_Mem
+                BX      LR
+
+                ALIGN
+
+                ENDIF
+
+
+                END

+ 504 - 0
src/calibration.c

@@ -0,0 +1,504 @@
+#include <string.h>
+#include "LabArm.h"
+
+uint8_t MenuMode;
+typedef struct
+{
+  uint32_t Adc;
+  int16_t Dac; /* 1 - 4095 */
+  int16_t Out; /* 1 - 9999 */
+  int8_t DotPosition; /* 0,1,2,3,4 */
+  int8_t Ok; /* Fiilled flag */
+  int16_t Res;
+}Calibration_t;
+
+
+
+Calibration_t VMax;
+Calibration_t VMin;
+Calibration_t IMax;
+Calibration_t IMin;
+static Calibration_t* Cur;
+static char* Text;
+__IO static uint32_t* CurDac;
+uint8_t  Contrast;
+MenuFunction_t  AfterContrast;
+
+void ContrastMenu(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+//    Contrast = CurrentSettings.VoltageDAC>>12;
+    LcdBlank(); /* Clear screen */
+    LcdChr(Y_POSITION*1 + X_POSITION * 0 + 14, "Contrast");
+    LcdChr(Y_POSITION*4 + X_POSITION * 0 + 14, "Press");
+    LcdChr(Y_POSITION*5 + X_POSITION * 0 + 14, "to exit");
+    goto redraw;
+  }
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        if ( Contrast < 15 )
+          Contrast++;
+        break;
+      case KEY_DOWN:
+        if ( Contrast > 0 )
+          Contrast--;
+        break;
+      case KEY_ENTER:
+        LcdContrast ( Contrast*3+42 );
+        CurrentFunc(AfterContrast);
+         return;
+    }
+redraw:
+    LcdContrast ( Contrast*3+42 );
+    {
+      char Buf[2];
+      uint8_t Counter = Contrast;
+      
+      Buf[0] = Counter/10 + '0';
+      Counter = Counter%10;
+      Buf[1] = Counter + '0';
+      LcdChr(BIG_UP+Y_POSITION*2 + X_POSITION * 2 + 2, Buf);
+      LcdChr(BIG_DOWN+Y_POSITION*3 + X_POSITION * 2 + 2, Buf);
+    }
+  }
+}
+
+
+static float Offset(float YMax, float YMin, float XMax, float XMin)
+{
+  return (YMin*XMax - YMax*XMin)/(XMax-XMin);
+}
+static void CalibrationToConfig(Config_t* Config)
+{
+  int i;
+
+  Config->V.ADCRamp = ((float)(VMax.Out - VMin.Out))/(VMax.Adc-VMin.Adc);
+  Config->V.ADCOffset = Offset(VMax.Out, VMin.Out, VMax.Adc, VMin.Adc);
+  Config->I.ADCRamp = ((float)(IMax.Out - IMin.Out))/(IMax.Adc-IMin.Adc);
+  Config->I.ADCOffset = Offset(IMax.Out, IMin.Out, IMax.Adc, IMin.Adc);
+  Config->V.DACRamp = ((float)(VMax.Out - VMin.Out))/(VMax.Dac-VMin.Dac);
+  Config->V.DACOffset = Offset(VMax.Out, VMin.Out, VMax.Dac, VMin.Dac);
+  Config->I.DACRamp = ((float)(IMax.Out - IMin.Out))/(IMax.Dac-IMin.Dac);
+  Config->I.DACOffset = Offset(IMax.Out,IMin.Out,IMax.Dac, IMin.Dac);
+  Config->V.DotPosition = VMax.DotPosition;
+  Config->I.DotPosition = IMax.DotPosition;
+  
+  /* Check menu correctness */
+  for (i=0; i<12; i++)
+  {
+    UserMenu_t* Menu = &CurrentConfig.Menu[i];
+    if ( (Menu->IVFlag == MENU_I_FLAG && IMax.DotPosition != Menu->DotPosition ) ||
+         (Menu->IVFlag == MENU_V_FLAG && VMax.DotPosition != Menu->DotPosition ) ) /* Dot position was changed */
+    {
+      memset(Menu, 0xFF, sizeof(*Menu)); /* Clear the menu */
+    }
+  }
+}
+
+
+void OutValue(uint8_t Y, uint8_t X, uint16_t Num, uint8_t DotPosition, uint8_t SelectPos)
+{
+  int i;
+  int Div = 1000;
+  uint8_t DisplayFlag = 0;
+  
+  for(i=0; i<4; i++)
+  {
+    char Chr;
+    uint32_t Light = 0;
+
+    if (i == DotPosition )
+    {
+      LcdChr ( Y_POSITION*(Y)+X_POSITION*X+1, " " );
+      LcdChr ( Y_POSITION*(Y+1)+X_POSITION*X+1, "." );
+      X=X+1;
+      DisplayFlag++;
+    }
+
+    if ( DisplayFlag == 0 && i == (DotPosition - 1))
+    {
+      DisplayFlag++;
+    }
+
+    Chr = Num / Div;
+    if ( DisplayFlag == 0 && Chr == 0)
+      Chr = ' ';
+    else
+    {
+      DisplayFlag = 1;
+      Chr = Chr + '0';
+    }
+    if (i == SelectPos)
+    {
+      Light = INVERSE;
+    } 
+    LcdChr (Light + Y_POSITION*Y+X_POSITION*X+BIG_UP+1, &Chr );
+    LcdChr (Light + Y_POSITION*(Y+1)+X_POSITION*X+BIG_DOWN+1, &Chr );
+    X = X + 2;
+    Num = Num % Div;
+    Div = Div / 10;
+  }
+  if ( DotPosition == 4)
+  {
+    LcdChr ( Y_POSITION*(Y)+X_POSITION*X+1, " " );
+    LcdChr ( Y_POSITION*(Y+1)+X_POSITION*X+1, " " );
+  }
+}
+
+void OutValueSmall(uint8_t Y, uint8_t X, uint16_t Num, uint8_t DotPosition, uint8_t InverseFlag)
+{
+  int i;
+  int Div = 1000;
+  uint32_t Light = InverseFlag?INVERSE:0;
+  uint8_t DisplayFlag=0;
+  
+  for(i=0; i<4; i++)
+  {
+    char Chr;
+
+    if (i == DotPosition)
+    {
+      DisplayFlag = 1;
+      LcdChr ( Light + Y_POSITION*(Y)+X_POSITION*X+1, "." );
+      X=X+1;     
+      DisplayFlag++;
+    }
+
+    if ( DisplayFlag == 0 && i == (DotPosition - 1))
+    {
+      DisplayFlag++;
+    }
+
+    Chr = Num / Div;
+    if ( DisplayFlag == 0 && Chr == 0)
+      Chr = ' ';
+    else
+    {
+      DisplayFlag = 1;
+      Chr = Chr + '0';
+    }
+    LcdChr (Light + Y_POSITION*Y+X_POSITION*X+1, &Chr );
+    X = X + 1;
+    Num = Num % Div;
+    Div = Div / 10;
+  }
+  if ( DotPosition == 4)
+  {
+    LcdChr ( Y_POSITION*(Y)+X_POSITION*X+1, " " );
+  }
+}
+
+
+void CalibrationMenuInternal(void);
+
+void SaveMenu(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    MenuMode = 0;
+    LcdBlank(); /* Clear screen */
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        if ( MenuMode == 0 )
+          MenuMode = 1;
+        break;
+      case KEY_DOWN:
+        if ( MenuMode != 0 )
+          MenuMode = 0;
+        break;
+      case KEY_ENTER:
+        if (MenuMode != 0)
+        {
+          SaveConfig();
+        }
+        CurrentFunc(StartFunction);
+        return;
+    }
+  }
+  else
+    return;
+
+redraw:
+  LcdChr(X_POSITION*0+Y_POSITION*1+14 + (0==MenuMode)*INVERSE, "Save to memory");
+  LcdChr(X_POSITION*0+Y_POSITION*2+14 + (1==MenuMode)*INVERSE, "Save to flash");
+}
+
+
+void SetDac(void);
+
+void SelectDot(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        Cur->DotPosition++;
+        if (Cur->DotPosition>4)
+          Cur->DotPosition = 4;
+        goto redraw;
+      case KEY_DOWN:
+        Cur->DotPosition--;
+        if (Cur->DotPosition<0)
+          Cur->DotPosition = 0;
+        goto redraw;
+      case KEY_ENTER:
+        CurrentFunc(SetDac);
+    }
+  }
+
+  return;
+redraw:
+  LcdChr(X_POSITION*0+Y_POSITION*1+14, Text);
+  LcdChr(X_POSITION*0+Y_POSITION*2+14, "Set Dot");
+  OutValue(3, 1, Cur->Out, Cur->DotPosition, 0xFF);
+}
+
+int16_t IncrementMul;
+int8_t  ChangeNumberFlag;
+void SetOut(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuMode = 0;
+    IncrementMul = 1000;
+    ChangeNumberFlag = 0;
+    LcdChr(X_POSITION*0+Y_POSITION*1+14, Text);
+    LcdChr(X_POSITION*0+Y_POSITION*2+14, "Set Out");
+    LcdChr(X_POSITION*0+Y_POSITION*5+14, "Longkey to end");
+    goto redraw;
+  }
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        if ( ChangeNumberFlag )
+        {
+          Cur->Out += IncrementMul;
+          goto redraw;
+        }
+        else
+        {
+          if ( MenuMode < 3 )
+          {
+            IncrementMul /= 10;
+            MenuMode++;
+            goto redraw;
+          }
+        }
+        return;
+      case KEY_DOWN:
+        if ( ChangeNumberFlag )
+        {
+          Cur->Out -= IncrementMul;
+          goto redraw;
+        }
+        else
+        {
+          if ( MenuMode > 0 )
+          {
+            IncrementMul *= 10;
+            MenuMode--;
+            goto redraw;
+          }
+        }
+        return;
+      case KEY_ENTER:
+        if ( ChangeNumberFlag == 0 )
+          ChangeNumberFlag = 1;
+        else
+          ChangeNumberFlag = 0;
+        return;
+    }
+  }
+  if ( (Event & EV_MASK) == EV_KEY_LONG )
+  {
+    Cur->Ok = 1;
+    CurrentFunc(CalibrationMenuInternal);
+  }
+
+  return;
+
+redraw:
+  OutValue(3, 1, Cur->Out, Cur->DotPosition, MenuMode);
+}
+
+void WaitMeasure(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    LcdChr(X_POSITION*0+Y_POSITION*3+14, "Please wait");
+    MenuMode = 0;
+    return;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED &&
+       (Event & KEY_MASK) == KEY_ADC ) /* End of conversion */
+  {
+    if (MenuMode == 2)
+    {
+      CurrentFunc(SetOut);
+      if ( Cur == &VMax || Cur == &VMin )
+        Cur->Adc = ADCVoltage;
+      else
+        Cur->Adc = ADCCurrent;
+    }
+    MenuMode++;
+  }
+}
+
+void SetDac(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    LcdChr(X_POSITION*0+Y_POSITION*1+14, Text);
+    LcdChr(X_POSITION*0+Y_POSITION*2+14, "Set Out");
+    EncCounter = Cur->Dac;
+    goto redraw;
+  }
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+      case KEY_DOWN:
+        Cur->Dac = EncCounter;
+        goto redraw;
+   case KEY_ENTER:
+        CurrentFunc(WaitMeasure);
+    }
+  }
+
+  return;
+redraw:
+  OutValue(3, 1, Cur->Dac, 4,  0xFF);
+  *CurDac = Cur->Dac;
+  OutValueSmall(5,0, EncStep, 4, 0);
+}
+
+
+void CalibrationMenu(void)
+{
+  /* Init The calibration */
+  VMax.Ok = 0;
+  VMin.Ok = 0;
+  IMax.Ok = 0;
+  IMin.Ok = 0;
+  CurrentFunc(CalibrationMenuInternal);
+}
+
+void CalibrationMenuInternal(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    MenuMode = 0;
+    LcdBlank(); /* Clear screen */
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_DOWN:
+        if ( MenuMode == 0 )
+          MenuMode = 4;
+        else
+          MenuMode = MenuMode - 1;
+        goto redraw;
+      case KEY_UP:
+        if ( MenuMode == 4 )
+          MenuMode = 0;
+        else
+          MenuMode = MenuMode + 1;
+        goto redraw;
+      case KEY_ENTER:
+        switch(MenuMode)
+        {
+          case 0:
+            Cur = &VMax;
+            Text = "Max Voltage";
+            Cur->Dac = 3700;
+            Cur->Out = 5555;
+            Cur->DotPosition = 2;
+            CurDac = &DAC_V;
+            CurrentFunc(SelectDot);
+            return;
+          case 1:
+            if (VMax.Ok == 0 ) /* The max value has not been set */
+              return;
+            Cur = &VMin;
+            Text = "Min Voltage";
+            Cur->Dac = 350;
+            Cur->Out = 555;
+            Cur->DotPosition = VMax.DotPosition;
+            CurDac = &DAC_V;
+            CurrentFunc(SetDac);
+            return;
+          case 2:
+            Cur = &IMax;
+            Text = "Max Current";
+            Cur->Dac = 3700;
+            Cur->Out = 5555;
+            Cur->DotPosition = 2;
+            CurDac = &DAC_I;
+            DAC_V = 2000; /* Set intermidiate voltage value !!! */
+            CurrentFunc(SelectDot);
+            return;
+          case 3:
+            if (IMax.Ok == 0 ) /* The max value has not been set */
+              return;
+            Cur = &IMin;
+            Text = "Min Current";
+            Cur->Dac = 350;
+            Cur->Out = 555;
+            Cur->DotPosition = IMax.DotPosition;
+            CurDac = &DAC_I;
+            CurrentFunc(SetDac);
+            return;
+          default:
+            if ( VMax.Ok && VMin.Ok && IMax.Ok && IMin.Ok )
+            {
+              CalibrationToConfig(&CurrentConfig);
+              CurrentFunc(SaveMenu);
+            }
+            return;
+        }
+    }
+  }
+  return;
+  
+redraw:
+  LcdChr(X_POSITION*0+Y_POSITION*1+14 + (0==MenuMode)*INVERSE, "Max Voltage");
+  LcdChr(X_POSITION*0+Y_POSITION*2+14 + (1==MenuMode)*INVERSE, "Min Voltage");
+  LcdChr(X_POSITION*0+Y_POSITION*3+14 + (2==MenuMode)*INVERSE, "Max Current");
+  LcdChr(X_POSITION*0+Y_POSITION*4+14 + (3==MenuMode)*INVERSE, "Min Current");
+  if ( VMax.Ok && VMin.Ok && IMax.Ok && IMin.Ok )
+  {
+    LcdChr(X_POSITION*0+Y_POSITION*5+14 + (4==MenuMode)*INVERSE, "Ok");
+  }
+  else
+  {
+    LcdChr(X_POSITION*0+Y_POSITION*5+14 + (4==MenuMode)*INVERSE, "");
+  }
+}

+ 299 - 0
src/clock.c

@@ -0,0 +1,299 @@
+#if defined(CLOCK_ENABLED)
+#include "LabArm.h"
+
+#define CLOCK_DIV (0x8000 - 2)
+
+void DisplayClock(uint32_t RTCVal, uint16_t InvPos, uint32_t Position)
+{
+  int Sec = RTCVal % (24*60*60); /* Seconds in the day */
+  int Value;
+  char Ch;
+  
+  if (Sec % 2 )
+    Ch = ' ';
+  else
+    Ch = ':';
+  LcdChr(Position + X_POSITION*2 + 1, &Ch );
+
+  if ( ((int16_t)InvPos) < 0 )
+  {
+    LcdChr(Position + X_POSITION*5 + 1, &Ch );
+    Value = Sec % 60; /* Seconds */
+    Ch = Value/10 + '0';
+    LcdChr( Position + X_POSITION*6 + 1, &Ch );
+    Ch = Value%10 + '0';
+    LcdChr( Position + X_POSITION*7 + 1, &Ch );
+  }
+  InvPos &= ~0x8000;
+  Value = Sec/(60*60); /* Hour */
+  Ch = Value/10 + '0';
+  LcdChr( Position + 1 + ((InvPos==0)?INVERSE:0), &Ch );
+  Ch = Value%10 + '0';
+  LcdChr( Position + X_POSITION*1 + 1 +((InvPos==1)?INVERSE:0), &Ch );
+  Sec = Sec%(60*60); /* Sec in the hour*/
+  Value = Sec/60; /* Minutes */
+  Ch = Value/10 + '0';
+  LcdChr( Position + X_POSITION*3 + 1 + ((InvPos==2)?INVERSE:0), &Ch );
+  Ch = Value%10 + '0';
+  LcdChr( Position + X_POSITION*4 + 1 + ((InvPos==3)?INVERSE:0), &Ch );
+  return;
+}
+
+
+int SwitchOnTheClock(void)
+{
+  if ( (RCC->BDCR & RCC_BDCR_RTCSEL) != RCC_BDCR_RTCSEL_0  ) /* RTC clock is off */
+  {
+    GPIOC->CRH |= GPIO_CRH_MODE14_1 |GPIO_CRH_MODE15_1; /* Out 2 MHZ*/
+    GPIOC->CRH &= ~(GPIO_CRH_CNF14|GPIO_CRH_CNF15); /* PUSH pull */
+    Delay(30);
+    GPIOA->BSRR =  GPIO_BSRR_BS14; /* Boot the cristal */
+    Delay(30);
+    GPIOC->CRH &= ~(GPIO_CRH_MODE14_1|GPIO_CRH_MODE15_1); /* Analog IN */
+    RCC->BDCR |= RCC_BDCR_LSEON;
+    {
+      __IO int i=1000000;
+      while ( i != 0 && (RCC->BDCR & RCC_BDCR_LSERDY) == 0)
+        i--;
+    }
+    if ( (RCC->BDCR & RCC_BDCR_LSERDY) == 0 )
+      return -1;
+  
+    RCC->BDCR |= RCC_BDCR_RTCSEL_0; /* RTC source is LSE */
+    RCC->BDCR |= RCC_BDCR_RTCEN; /* Enable RTC */
+
+    BKP->RTCCR = 63; 
+
+    RTC->CRL |= RTC_CRL_CNF; /* Enter config mode */
+    RTC->PRLL = CLOCK_DIV; /* Freq is some higher that 1 hz */
+    BKP->DR7  = CLOCK_DIV;
+    RTC->CRL &= ~RTC_CRL_CNF; /* Write value  */
+    while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+      ;
+  }
+
+  RTC->CRL &= ~RTC_CRL_RSF; /* This bit will be set at syncronisation time */
+  while ( (RTC->CRL & RTC_CRL_RSF) == 0)
+    ; /* Wait syncronisation */
+  while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+    ;
+
+  RTC->CRH |= RTC_CRH_SECIE; /* Enable second interrupt */
+  while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+    ;
+  NVIC->ISER[(RTC_IRQn >> 0x05)] =	(u32)0x01 << (RTC_IRQn & (u8)0x1F); /* Enable RTC second interrupt */
+
+  return 0;
+}
+
+void RTC_IRQHandler(void)
+{
+  if ((EventQueue & ~KEY_ADC) == 0 ) /* ADC has lower priority */
+    EventQueue = EV_KEY_PRESSED | KEY_CLOCK;
+  RTC->CRL &= ~RTC_CRL_SECF;
+  if (TimerValue != 0)
+  {
+    if (RemainTimerValue > 1)
+    {
+      if ( RemainTimerValue != 0xFFFF )
+        RemainTimerValue--;     
+    }
+    else
+    {
+      RemainTimerValue = 0xFFFF;
+      DAC_V = 0; /* Off the output */
+      DAC_I = 0;
+    }
+  }
+}
+
+static int32_t NewTime;
+static uint8_t Change;
+
+static int Inc;
+void SetupTheClock(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = 0;
+    Change = 0;
+    Inc = 1;
+    NewTime = (((RTC->CNTH<<16) + RTC->CNTL)%(24*60*60) + (24*60*60));
+    NewTime = NewTime / 60*60; /* Round to 1 minutes */
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+       case KEY_UP:
+        if ( Change == 0 )
+        {
+          if ( MenuPos == 6 )
+            MenuPos = 0;
+          else
+            MenuPos = MenuPos + 1;
+          goto redraw;
+        }
+        else
+          Inc = 1;
+      goto ChangeTime;
+      case KEY_DOWN:
+        if ( Change == 0 )
+        {
+          if ( MenuPos == 0 )
+            MenuPos = 6;
+          else
+            MenuPos = MenuPos - 1;
+          goto redraw;
+        }
+        else
+          Inc = -1;
+        goto ChangeTime;
+      case KEY_ENTER:
+        switch(MenuPos)
+        {
+          case 0:
+          case 1:
+          case 2:
+          case 3:
+            Change = !Change;
+            return;
+          case 4:
+            BKP->DR5 = (NewTime >> 16); 	/* 05.10.11 Clear calibration time to prevent bad next calibration. */
+    		    BKP->DR6 = NewTime; 
+            
+            /* Restore default clock speed 18.10.11 */
+            BKP->RTCCR = 63; 
+            BKP->DR7  = CLOCK_DIV;
+
+            RTC->CRL |= RTC_CRL_CNF; /* Enter config mode */
+            RTC->PRLL = CLOCK_DIV; /* Freq is some higher that 1 hz */
+            while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+              ;
+            goto SaveTime;
+          case 5:
+            CurrentFunc(StartFunction);
+            return;
+          case 6: /* Calibration */
+            if (BKP->DR5 != 0 || BKP->DR6 != 0 ) /* Trim the clock */
+            {  /* Trim the clock speed */
+              int Delta;
+              int Time;
+              Delta = NewTime%(24*60*60) - (((RTC->CNTH<<16) + RTC->CNTL)%(24*60*60)); /* New time - old time. Positive is slow */
+              Time  = ((RTC->CNTH<<16) + RTC->CNTL) - ((BKP->DR5<<16) +  BKP->DR6); /* Old time - last trim date */
+              if ( Delta > (12*60*60) )
+                Delta = Delta - (24*60*60);
+              if ( Delta < -(12*60*60) )
+                Delta = Delta + (24*60*60);
+              if (Time > (24*60*60 - 60*10) ) /* Time eplased is More then 1 day without 10 minutes */
+              {
+#if !defined(__GNUC__)
+                Delta = (Delta * ((int64_t)1024*1024))/Time; /* Delta in PPM */
+#else
+                /* Int64 operationd adds about 9k of code!!! */		
+                while ( Delta > 2047 || Delta < -2047 )
+                {
+                  Delta = Delta/2;
+                  Time  = Time/2;
+                }
+                Delta = (Delta * (1024*1024))/Time;
+#endif
+                Delta = (BKP->RTCCR & BKP_RTCCR_CAL) - Delta;
+                if (Delta < 0 || Delta > 127 )
+                {
+                  BKP->DR7  = BKP->DR7 + Delta/32 - 2; /* Save new prescaler value */
+                  RTC->CRL |= RTC_CRL_CNF; /* Enter config mode */
+                  RTC->PRLL = BKP->DR7; /* Set new prescaler value. PRLL is read only */
+                    RTC->CRL &= ~RTC_CRL_CNF; /* Write value  */
+                    while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+                        ;
+                  Delta = 64 + (Delta - Delta/32*32);
+                }
+                BKP->RTCCR = Delta; /* Write correction */
+              }
+            }
+            BKP->DR5 = (NewTime >> 16); 
+            BKP->DR6 = NewTime;
+            goto SaveTime;
+        }
+    }
+  }
+  return;
+
+SaveTime:
+    RTC->CRL |= RTC_CRL_CNF; /* Enter config mode */
+    RTC->CNTH = NewTime>>16;
+    RTC->CRL &= ~RTC_CRL_CNF; /* Write value  */
+    while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+      ;
+    RTC->CRL |= RTC_CRL_CNF; /* Enter config mode */
+    RTC->CNTL = NewTime&0xFFFF;
+    RTC->CRL &= ~RTC_CRL_CNF; /* Write value  */
+    while ( (RTC->CRL & RTC_CRL_RTOFF) == 0 ) /* Wait to ready for writing */
+      ;
+    CurrentFunc(StartFunction);
+    return;
+
+ChangeTime:
+  switch (MenuPos)
+  {
+    case 0: 
+        Inc = Inc * 10 * 60 * 60;  /* +-10 hours */
+        break;
+    case 1: 
+        Inc = Inc * 60 * 60;  /* +-1 hours */
+        break;
+    case 2:
+        Inc = Inc * 10 * 60;  /* +-10 min */
+        break;
+    case 3:
+        Inc = Inc * 60;  /* +-1 min */
+        break;
+  }
+  if ( NewTime + Inc < 24*60*60 )
+    return;
+  if ( NewTime + Inc >= ( 24*60*60*2 - 1 ) ) /* 2 day without 1 sec */
+    return;
+  NewTime += Inc;
+
+redraw:
+  DisplayClock(NewTime, MenuPos, Y_POSITION*3+X_POSITION*9);
+  LcdChr (Y_POSITION*4+X_POSITION*0+14+(4==MenuPos)*INVERSE, "Set time" );
+  LcdChr (Y_POSITION*5+X_POSITION*0+14+(5==MenuPos)*INVERSE, "Return" );
+  LcdChr (Y_POSITION*1+X_POSITION*0+14+(6==MenuPos)*INVERSE, "Calibration" );
+}
+
+
+uint16_t TimerValue = 0;
+uint16_t RemainTimerValue = 0;
+void TimerSetup(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    EncCounter = TimerValue;
+    EncStepMax = 1800;
+    EncMax = 18 * 3600 + 1;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    if ( (Event & KEY_MASK) == KEY_ENTER )
+    {
+      TimerValue = EncCounter;
+      EncStepMax = ENC_STEP_MAX_DEFAULT;
+      EncMax = ENC_MAX_DEFAULT;
+      CurrentFunc(StartFunction);
+      return;
+    }
+  }
+
+  LcdChr(Y_POSITION*1+X_POSITION*0+14, "Timer value" );
+  DisplayClock(EncCounter, 10+0x8000, Y_POSITION*3+X_POSITION*6);
+}
+
+
+#endif

+ 207 - 0
src/config.c

@@ -0,0 +1,207 @@
+#include <string.h>
+#include "LabArm.h"
+
+#if defined(REAL_FLASH)
+#if !defined(__GNUC__)
+const SavedDomain_t SavedDomain[2] __attribute__ ((at(FLASH_BASE+1024*13)));
+#else
+const SavedDomain_t SavedDomain[2] __attribute__ ((section(".eb0rodata")));
+//const SavedDomain_t SavedDomain[2] __attribute__ ((aligned(1024)))={{{{0}}}};
+#endif
+const Settings_t*  NextSettings;
+
+#define RDP_Key                  ((uint16_t)0x00A5)
+#define FLASH_KEY1               ((uint32_t)0x45670123)
+#define FLASH_KEY2               ((uint32_t)0xCDEF89AB)
+
+static uint32_t Crc(const void* Pointer, int Len)
+{
+  int i;
+  const uint8_t* Data = (const uint8_t*)Pointer;
+  uint32_t Ret = 0x55;
+
+#if 1
+  for (i = 0; i<Len; i++)
+  {
+    Ret = Ret ^ (uint32_t)(Data[i]);
+  }
+#else
+  CRC->CR |= CRC_CR_RESET;
+  CRC->IDR = Ret;
+  for (i = 0; i<Len; i++)
+  {
+    CRC->IDR = Data[i];
+    Ret = CRC->IDR;
+  }
+#endif
+
+  return Ret;
+}
+
+
+void WriteFlash(void* Src, void* Dst, int Len)
+{
+  uint16_t* SrcW = (uint16_t*)Src;
+  volatile uint16_t* DstW = (uint16_t*)Dst;
+
+  FLASH->KEYR = FLASH_KEY1;
+  FLASH->KEYR = FLASH_KEY2;
+
+  FLASH->CR |= FLASH_CR_PG; /* Programm the flash */
+  while (Len)
+  {
+    *DstW = *SrcW;
+    while ((FLASH->SR & FLASH_SR_BSY) != 0 )
+      ;
+    if (*DstW != *SrcW )
+    {
+      break;
+    }
+    DstW++;
+    SrcW++;
+    Len = Len - sizeof(uint16_t);
+  }
+
+  FLASH->CR &= ~FLASH_CR_PG; /* Reset the flag back !!!! */
+  FLASH->CR |= FLASH_CR_LOCK; /* Lock the flash back */
+}
+void ErasePage(void* Addr)
+{
+  FLASH->KEYR = FLASH_KEY1;
+  FLASH->KEYR = FLASH_KEY2;
+
+  FLASH->CR |= FLASH_CR_PER; /* Page erase */
+  FLASH->AR = (uint32_t)Addr; 
+  FLASH->CR|= FLASH_CR_STRT; /* Start erase */
+  while ((FLASH->SR & FLASH_SR_BSY) != 0 ) /* Wait end of eraze */
+    ;
+  FLASH->CR &= ~FLASH_CR_PER; /* Page erase end */
+}
+void SaveConfig(void)
+{
+  const SavedDomain_t* ValidDomain;
+
+  CurrentConfig.Crc = Crc(&CurrentConfig, sizeof(Config_t) - sizeof(uint32_t));
+
+  ValidDomain = SavedDomain;
+  /* Clear 2 page */
+  ErasePage((void*)SavedDomain); 
+  ErasePage((void*)(SavedDomain+1)); 
+
+  /* Save header and first settings */
+  WriteFlash(&CurrentConfig, (void*)&ValidDomain->Config, sizeof(Config_t));
+
+  NextSettings = &ValidDomain->Settings[0];
+  SaveSettings(1);
+  NextSettings = &ValidDomain->Settings[1]; /* Prepare pointer to save at power fault */
+}
+
+void SaveSettings(uint16_t AndFlash)
+{
+  if ( (DAC->DOR1 != 0 &&  DAC->DOR2 != 0) ) /* Is the out ON */
+  {
+    CurrentSettings.VoltageDAC = DAC->DOR1; /* Restore real current value */
+    CurrentSettings.CurrentDAC = DAC->DOR2;
+  }
+  /* Else - save preset values */
+
+  CurrentSettings.CurrentDAC |= Flags; /* Adding flags to save */
+  CurrentSettings.VoltageDAC |= (Contrast<<12);
+  if (Flags & BACKUP_SAVE_FLAG)
+  {
+    BKP->DR10 = CurrentSettings.VoltageDAC;
+    BKP->DR9  = CurrentSettings.CurrentDAC;
+    if (AndFlash == 0 )
+      goto Restore;
+  }
+  if ( NextSettings  == NULL )
+    return;
+  WriteFlash(&CurrentSettings, (void*)NextSettings, sizeof(CurrentSettings));
+
+Restore:
+  CurrentSettings.CurrentDAC &= 0x0FFF; /* Remove flags back */
+  CurrentSettings.VoltageDAC &= 0x0FFF;
+}
+
+
+int RestoreConfig(void) /* Return !0 if failed */
+{
+  const Settings_t* CurSettings;
+  const Settings_t* PrevSettings;
+  const SavedDomain_t* ValidDomain;
+ 
+  ValidDomain = SavedDomain;
+  if ( Crc( &ValidDomain->Config, sizeof(Config_t) - sizeof(uint32_t) ) != ValidDomain->Config.Crc )
+  {
+    ValidDomain = SavedDomain + 1;
+    if ( Crc( &ValidDomain->Config, sizeof(Config_t) - sizeof(uint32_t) ) != ValidDomain->Config.Crc )
+    {
+      return -1; /* Both blocks are invalid */
+    }
+  }
+  memcpy((char*)&CurrentConfig, (char*)&ValidDomain->Config, sizeof(CurrentConfig));
+
+  PrevSettings = ValidDomain->Settings;
+
+  for ( CurSettings = PrevSettings; CurSettings < ValidDomain->Settings + SAVED_SETTINGS_COUNT; CurSettings++)
+  {
+    if ( CurSettings->VoltageDAC == 0xFFFF && CurSettings->CurrentDAC == 0xFFFF )
+	  break;
+    PrevSettings = CurSettings;
+  }
+
+  memcpy(&CurrentSettings, PrevSettings, sizeof(CurrentSettings));
+#if 0
+  Flags = CurrentSettings.CurrentDAC & 0xF000;
+  CurrentSettings.CurrentDAC &= 0x0FFF;
+  Contrast = CurrentSettings.VoltageDAC>>12;
+  CurrentSettings.VoltageDAC &= 0x0FFF;
+#endif
+
+  NextSettings = CurSettings;
+  if ( CurSettings == ValidDomain->Settings + SAVED_SETTINGS_COUNT ) // All space is full
+  {
+    const SavedDomain_t* NextDomain = SavedDomain;
+
+    if ( ValidDomain == SavedDomain )
+      NextDomain = SavedDomain + 1;
+
+    WriteFlash((void*)&ValidDomain->Config, (void*)&NextDomain->Config, sizeof(Config_t));
+    WriteFlash((void*)CurSettings, (void*)&NextDomain->Settings[0], sizeof(Settings_t));
+    NextSettings = &NextDomain->Settings[1];
+    ErasePage( (void*) ValidDomain );
+    ValidDomain = NextDomain;
+  }
+  
+  if ( BKP->DR9 & BACKUP_SAVE_FLAG )
+  {
+    /* restore from backup domain */
+    CurrentSettings.VoltageDAC = BKP->DR10;
+    CurrentSettings.CurrentDAC = BKP->DR9;
+  }
+
+  Flags = CurrentSettings.CurrentDAC&0xF000;
+  CurrentSettings.CurrentDAC &= 0x0FFF;
+  Contrast = CurrentSettings.VoltageDAC>>12;
+  CurrentSettings.VoltageDAC &= 0x0FFF;
+  LcdContrast ( Contrast*3+42 );
+
+  return 0;
+}
+
+#else  /* REAL_FLASH */
+void SaveConfig(void)
+{
+}
+
+int RestoreConfig(void)
+{
+  return -1;
+}
+
+void SaveSettings(void)
+{
+}
+
+#endif /* RAL_FLASH */
+

+ 577 - 0
src/event.c

@@ -0,0 +1,577 @@
+#include <string.h>
+#include "event.h"
+
+uint16_t Event;     // Generated event
+volatile uint8_t EvCounter; // Press delay counter
+
+MenuFunction_t CurrentFunction = NULL; // Current function
+MenuFunction_t PrevFunc = NULL;
+
+volatile uint16_t EventQueue; // Generated event
+static uint16_t PrevKey; // Previous keys pressed
+static uint8_t  RealizeCounter; // Realize delay counter
+
+static uint16_t PrevCounter;
+void EventInit(void)
+{
+  NVIC->ISER[(TIM3_IRQn >> 0x05)] =	(u32)0x01 << (TIM3_IRQn & (u8)0x1F); /* Enable Timer interrupt */
+  NVIC->ISER[(TIM2_IRQn >> 0x05)] =	(u32)0x01 << (TIM2_IRQn & (u8)0x1F); /* Enable Timer interrupt */
+
+  TIM3->DIER |= TIM_DIER_UIE; /* enable update IRQ */
+  TIM3->ARR = 10000; /* 2MHz / 10000 = 200Hz  - 5mSec */
+  TIM3->CR1 |= TIM_CR1_CEN;    /* start TIM7 */
+  
+//  TIM2->CNT = 0x8000;
+  TIM2->ARR = 0xFFFF;
+  TIM2->SMCR |= (TIM_SMCR_SMS_0|TIM_SMCR_SMS_1); /* encoder mode 2 */
+//  TIM2->CCMR1 |= TIM_CCMR1_CC1S_0|TIM_CCMR1_IC1F|TIM_CCMR1_CC2S_0|TIM_CCMR1_IC2F;  /* TI1, TI2 - inputs with max filter */
+//  TIM2->CCER |= TIM_CCER_CC1E|TIM_CCER_CC2E; /* Enable inputs */
+  TIM2->SR = 0;
+  TIM2->DIER |= TIM_DIER_CC2IE|TIM_DIER_CC1IE; /* Interrupt by trigger */
+  TIM2->CCR1 = (uint16_t)- 4;
+  TIM2->CCR2 = 0 + 4;
+  PrevCounter = 0;
+  TIM2->CR1 |= TIM_CR1_CEN;
+}
+
+void EventKeys(void)
+{
+  uint16_t Key;
+  
+  if ( (EventQueue & KEY_MASK_SYS) != 0 ) /* Previous event hasn't been handled. The key hase highiest  priority */
+    return;
+  if (CurrentFunction != PrevFunc ) /* The function was changed */
+    return;
+  Key = (~KEYPORT) & KEY_MASK_SYS; /* Read the port */
+
+  if ( Key == 0 ) // no any key pressed
+  {
+    if ( PrevKey == 0 ) // no any key was pressed before
+    {
+      EvCounter = 0;
+      return;
+    }
+    if ( EvCounter > KEY_PRESSED_VALUE )
+    {
+      RealizeCounter++; // increase timer counter
+      if ( RealizeCounter > KEY_REALIZE_VALUE ) // expired realise timeout
+      {
+        if ( EvCounter != 0xFF ) /* There is no switch to new function. New function should not get previos function event */
+        {
+          EventQueue = EV_KEY_REALIZED | PrevKey; /* Realized event - the last event */
+        }
+        EvCounter = 0; // Reset interval timer value
+        PrevKey = 0;   // Reset key pressed value
+        RealizeCounter = 0;  // Reset realise counter
+      }
+    }
+    else
+    {
+      EvCounter = 0; // Reset interval timer value
+      PrevKey = 0;   // Reset key pressed value
+      RealizeCounter = 0;  // Reset realise counter
+    }
+  }
+  else // Some keys are pressed
+  {
+    RealizeCounter = 0; //reset realise delay
+   
+    if ( EvCounter == 0xFF ) /* Locked - new function has been set */
+      return; 
+    
+    if ( Key & (~PrevKey) ) //there are some new keys
+    {
+      PrevKey |= Key;       // adding the new keys
+      if ( EvCounter == 0 )
+      {
+      /* Generate KEY TOUCH event */
+        EventQueue = EV_KEY_TOUCH + PrevKey;
+      }
+      else if ( EvCounter > KEY_LONG_VALUE ) // Delay after first press is not long
+        EventQueue = EV_KEY_LONG | PrevKey; //generate key press event
+    }
+    else // the same keys are pressed
+    {
+      if ( EvCounter == KEY_PRESSED_VALUE ) // Delay after first press is not long
+        EventQueue = EV_KEY_PRESSED | PrevKey; //generate key press event
+      else if ( EvCounter == KEY_LONG_VALUE )  // Long press timeout has expired
+      {
+        EventQueue = EV_KEY_LONG | PrevKey; // Generate Long press event
+      }
+      else if ( EvCounter == KEY_REPEATE_VALUE ) // After long press the key is stil pressed
+      {
+        EventQueue = EV_KEY_REPEATE | PrevKey; // Generate repeate press event
+        EvCounter = KEY_LONG_VALUE; // Reset time counter for next delay
+      }
+      EvCounter++; // Delay counter increasing
+    }
+  }
+}
+
+void EventCheck(void)
+{
+      if ( CurrentFunction != PrevFunc ) // Function was changed
+      {
+        Event = EV_FUNC_FIRST;       // Generate FUNC_FIRST event
+        PrevFunc = CurrentFunction;      // Save the function
+    		__disable_irq();
+        if ( EvCounter )             /// Some keys are stil pressed
+          EvCounter = 0xFF;            // Lock any key events until all keys are not realized
+    		__enable_irq();
+      }
+      else
+      {
+    		__disable_irq();
+        Event = EventQueue;          // Read event thar was generated in interrupt handlers
+        EventQueue = 0;              // The interrupt handlers can write new value
+    		__enable_irq();
+      }
+
+      CurrentFunction();      // Run the current menu function
+}
+
+static uint16_t TickCounter;
+static uint16_t PrevTickCounter;
+uint16_t EncCounter;
+
+void TIM3_IRQHandler(void)
+{
+  TIM3->SR = 0; /* Clear pending flag */
+  EventKeys();
+  TickCounter++;
+  if ( TickCounter - PrevTickCounter > 1024 ) /* 10 cek */
+    PrevTickCounter = TickCounter - 512; /* Once in 2.5 second */
+  if ( TickCounter - PrevTickCounter == 2 )
+  {
+	  TIM2->SR = 0; /* Clear pending flag */
+  	TIM2->DIER |= TIM_DIER_CC2IE; /* Interrupt by trigger */
+  }   
+}
+
+uint16_t EncStep;
+uint16_t EncStepMax = ENC_STEP_MAX_DEFAULT;
+uint16_t EncMax = ENC_MAX_DEFAULT;
+
+void TIM2_IRQHandler(void)
+{
+  static uint16_t Delta;
+
+  PrevCounter = TIM2->CNT;
+  TIM2->CCR1 = PrevCounter - 4;
+  TIM2->CCR2 = PrevCounter + 4;
+  TIM2->SR = 0; /* Clear pending flag - only trigger interrupt*/
+
+  Delta = TickCounter - PrevTickCounter;
+  PrevTickCounter = TickCounter;
+  
+  Delta = Delta / 2;
+  if (EncStep == 0)
+    EncStep = 1;
+  if ( Delta < 8 && EncStep < EncStepMax) /* The step has to be inreased */
+  {
+    if ( EncStep < 4 )
+      EncStep = EncStep*2;
+    else if (EncStep < 64)
+      EncStep = EncStep + EncStep/4;
+    else
+      EncStep = EncStep + EncStep/8;
+  }
+  if ( Delta > 20 )
+  {
+    if (Delta > 120) /* 0.6 sec */
+      EncStep = 1;
+    EncStep = EncStep - EncStep/2;
+  }
+
+  if (TIM2->CR1 & TIM_CR1_DIR)
+  {
+    if ((int32_t)EncCounter - EncStep < 0 )
+      EncCounter = 0;
+    else
+      EncCounter -= EncStep;
+    if ( (EventQueue & KEY_MASK_SYS) == 0) /* Medium priority */
+      EventQueue = EV_KEY_PRESSED|KEY2; 
+  }
+  else
+  {
+    if ((uint32_t)EncCounter + EncStep > EncMax )
+      EncCounter = EncMax;
+    else
+      EncCounter += EncStep;
+    if ( (EventQueue & KEY_MASK_SYS) == 0) /* Medium priority */
+      EventQueue = EV_KEY_PRESSED|KEY3; 
+  }
+}
+
+
+#if defined(EVENT_DEMO)
+
+#include "n3310.h"
+
+void SystemInit()
+{
+}
+
+#if defined(ENC_DEMO)
+uint16_t Value;
+
+void Contrast(void)
+{
+}
+
+void StartFunction(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    Value = 0;
+    goto redraw;
+  }
+  if ( (Event & KEY2) || (Event & KEY3) )
+  {
+    Value = EncCounter;
+    LcdChr ( Y_POSITION*3+X_POSITION*1+9, "Encoder" );
+    goto redraw;
+  }
+
+  if ( (Event & KEY_MASK) == 0)
+    return;
+
+  if ( ((Event & EV_MASK) == EV_KEY_LONG) )
+  { 
+    LcdChr ( Y_POSITION*3+X_POSITION*1+9, "Long key" );
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_TOUCH )
+  { 
+    LcdChr ( Y_POSITION*3+X_POSITION*1+9, "Touch key" );
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_REALIZED)
+  { 
+    LcdChr ( Y_POSITION*3+X_POSITION*1+9, "Realize key" );
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_DOUBLE)
+  { 
+    LcdChr ( Y_POSITION*5+X_POSITION*1+9, "Double key" );
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  { 
+    LcdChr ( Y_POSITION*5+X_POSITION*1+9, "" );
+    LcdChr ( Y_POSITION*3+X_POSITION*1+9, "Key" );
+	Value = 1;
+    goto redraw;
+  }
+  
+  if ( (Event & EV_MASK) == EV_KEY_REPEATE )
+  {
+    LcdChr ( Y_POSITION*3+X_POSITION*1+9, "Repeate key" );
+    if ( Value < 9000 )
+   	  Value++;
+	goto redraw;
+  }
+
+  return;
+
+redraw:
+  {
+#define DIGIT_COUNT 4
+#define FIRST_DIV 1000
+    char Out[DIGIT_COUNT];
+  	int Div = FIRST_DIV;
+  	char* Pointer = Out;
+  	char i = DIGIT_COUNT;
+  	char OnFlag = 0;
+	int Data = Value;
+
+  	while (i)
+  	{
+     char Dig = Data/Div;
+
+     if ( Dig == 0 && OnFlag == 0)
+     {
+       *Pointer = ' '; //First zero digits are not displaed
+     }
+     else
+     {
+       *Pointer = Dig + '0';
+       OnFlag = 1;
+     }
+     Pointer++;
+     i--;
+	 if ( i == 1 )
+	   OnFlag = 1; /* Zero also should be displayed */
+     Data = Data%Div;
+     Div = Div/10;
+  	} //while
+    LcdChr ( Y_POSITION*1+X_POSITION*1+DIGIT_COUNT+BIG_UP, Out );
+    LcdChr ( Y_POSITION*2+X_POSITION*1+DIGIT_COUNT+BIG_DOWN, Out );
+    LcdChr ( Y_POSITION*4+X_POSITION*1+9, "Menu Func" );
+  }
+}
+#endif /* ENC_DEMO */
+
+#if defined(MENU_DEMO)
+uint8_t MenuCounter;
+
+void MenuSelected(void);
+void Contrast(void);
+#if defined(FLOAT_DEMO)
+void FloatChange(void);
+#include "ftos.h"
+#endif
+
+void MainMenu()
+{
+  if ( Event == 0 )
+    return;
+
+  if ( Event == EV_FUNC_FIRST )
+  {
+    MenuCounter = 0;
+    goto RedrawMenu;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        if ( MenuCounter == 0 )
+          MenuCounter = 4;
+        else
+          MenuCounter = MenuCounter - 1;
+        break;
+      case KEY_DOWN:
+        if ( MenuCounter == 4 )
+          MenuCounter = 0;
+        else
+          MenuCounter = MenuCounter + 1;
+        break;
+      case KEY_ENTER:
+        switch(MenuCounter)
+        {
+          case 4:
+            CurrentFunc = Contrast;
+            break;
+#if defined(FLOAT_DEMO)
+          case 2:
+            CurrentFunc = FloatChange;
+            break;
+#endif
+          default:
+            CurrentFunc = MenuSelected;
+            return;
+        }
+    }
+  }
+  else
+    return;
+RedrawMenu:
+  LcdChr(X_POSITION*0+Y_POSITION*1+14 + (0==MenuCounter)*INVERSE, "Menu1");
+  LcdChr(X_POSITION*0+Y_POSITION*2+14 + (1==MenuCounter)*INVERSE, "Menu2");
+#if defined(FLOAT_DEMO)
+  LcdChr(X_POSITION*0+Y_POSITION*3+14 + (2==MenuCounter)*INVERSE, "Float demo");
+#else
+  LcdChr(X_POSITION*0+Y_POSITION*3+14 + (2==MenuCounter)*INVERSE, "Menu3");
+#endif  
+  LcdChr(X_POSITION*0+Y_POSITION*4+14 + (3==MenuCounter)*INVERSE, "Menu4");
+  LcdChr(X_POSITION*0+Y_POSITION*5+14 + (4==MenuCounter)*INVERSE, "Contrast");
+}
+
+void MenuSelected(void)
+{
+  if ( (Event&EV_MASK) == EV_FUNC_FIRST)
+  {
+    LcdChr(X_POSITION*0+Y_POSITION*1+14,  "Press key     ");
+    LcdChr(X_POSITION*0+Y_POSITION*2+14,  "ENTER for a   ");
+    LcdChr(X_POSITION*0+Y_POSITION*3+14,  "long time to  ");
+    LcdChr(X_POSITION*0+Y_POSITION*4+14,  "return main   ");
+    LcdChr(X_POSITION*0+Y_POSITION*5+14,  "menu");
+    return;
+  }
+  
+  if ( Event == (EV_KEY_LONG + KEY_ENTER) )
+  { /* Return back */
+    CurrentFunc = MainMenu;
+  } 
+}
+
+#if defined(FLOAT_DEMO)
+void FloatChange(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    EncCounter = 2000;
+    LcdClear(); /* Clear screen */
+    goto redraw;
+  }
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_ENTER:
+         CurrentFunc = MainMenu;
+         return;
+    }
+  }
+redraw:
+  {
+    char Buf[10];
+    float Out = EncCounter *0.00756 + 0.015;
+    
+    FToS(Out, Buf);
+      
+    LcdChr(Y_POSITION*2 + X_POSITION * 2 + 10, Buf);
+  }
+}
+#endif /* FLAT_DEMO */
+
+void Contrast(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    MenuCounter = 70;
+    LcdClear(); /* Clear screen */
+    goto redraw;
+  }
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        if ( MenuCounter < 90 )
+          MenuCounter++;
+        break;
+      case KEY_DOWN:
+        if ( MenuCounter > 0 )
+          MenuCounter--;
+        break;
+      case KEY_ENTER:
+         CurrentFunc = MainMenu;
+         return;
+    }
+redraw:
+    LcdContrast ( MenuCounter );
+    {
+      char Buf[2];
+      uint8_t Counter = MenuCounter;
+      
+      Buf[0] = Counter/10 + '0';
+      Counter = Counter%10;
+      Buf[1] = Counter + '0';
+      LcdChr(BIG_UP+Y_POSITION*2 + X_POSITION * 2 + 2, Buf);
+      LcdChr(BIG_DOWN+Y_POSITION*3 + X_POSITION * 2 + 2, Buf);
+    }
+  }
+}
+
+int LongCounter;
+
+void StartFunction(void)
+{
+  char* OutString = "";
+  char  KeyArray[3];
+  
+  if (Event == 0)
+    return;
+  
+  switch ( Event & EV_MASK )
+  {
+    case EV_KEY_TOUCH:
+      OutString = "Touch";
+//      return;
+      break;
+    case EV_KEY_PRESSED:
+      OutString = "Press";
+      break;
+    case EV_KEY_LONG:
+      OutString = "Long";
+      if ( Event & KEY_ENTER )
+	  {
+	    if (LongCounter > 3 )
+         CurrentFunc = MainMenu;
+        LongCounter++;
+	  }
+      break;
+    case EV_KEY_REPEATE:
+      OutString = "Repeate";
+      break;
+    case EV_KEY_REALIZED:
+      OutString = "Realize";
+      break;
+    case EV_KEY_DOUBLE:
+      OutString = "Double";
+      break;
+    case EV_FUNC_FIRST:
+      OutString = "First";
+  }
+    
+  LcdChr(Y_POSITION*(MenuCounter%5+1) + 14, OutString);
+  Event &= KEY_MASK;
+  KeyArray[0] = Event & KEY1 ? '*':'-';
+  KeyArray[1] = Event & KEY2 ? '*':'-';
+  KeyArray[2] = Event & KEY3 ? '*':'-';
+  LcdChr(Y_POSITION*(MenuCounter%5+1) + X_POSITION * 7 + 3, KeyArray);
+
+  {
+    uint8_t Counter = MenuCounter;
+    KeyArray[0] = Counter/100 + '0';
+    Counter = Counter%100;
+    KeyArray[1] = Counter/10 + '0';
+    Counter = Counter%10;
+    KeyArray[2] = Counter + '0';
+    LcdChr(Y_POSITION*(MenuCounter%5+1) + X_POSITION * 10 + 3, KeyArray);
+  }
+  MenuCounter++;
+}
+
+#endif /*MENU_DEMO*/
+
+
+
+int main()
+{
+  CurrentFunc(StartFunction);
+  /* init hardware */
+  SCB->VTOR = FLASH_BASE;
+//  NVIC->ISER[(TIM3_IRQn >> 0x05)] |=	(u32)0x01 << (TIM3_IRQn & (u8)0x1F); /* Enable Timer interrupt */
+//  NVIC->ISER[(TIM2_IRQn >> 0x05)] |=	(u32)0x01 << (TIM2_IRQn & (u8)0x1F); /* Enable Timer interrupt */
+  LcdInit();
+  LcdClear();
+
+  /* CLOCK = 8MHz / 8 = 1 MHz */
+  RCC->CFGR |= RCC_CFGR_HPRE_DIV2|RCC_CFGR_PPRE1_DIV4;
+  RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN |RCC_APB2ENR_AFIOEN;
+  RCC->APB1ENR |= RCC_APB1ENR_TIM3EN|RCC_APB1ENR_TIM2EN; 
+
+  GPIOB->CRH |= GPIO_CRH_MODE12_1|GPIO_CRH_MODE13_1|GPIO_CRH_MODE14_1|GPIO_CRH_MODE15_1; /* Output 2MHz */
+  GPIOB->CRH &= ~(GPIO_CRH_CNF12|GPIO_CRH_CNF13|GPIO_CRH_CNF14|GPIO_CRH_CNF15); /* Output Push-pull */
+
+#if 0   
+  TIM3->DIER |= TIM_DIER_UIE; /* enable update IRQ */
+  TIM3->ARR = 10000; /* 1MHz / 10000 = 100Hz  - 10mSec */
+  TIM3->CR1 |= TIM_CR1_CEN;    /* start TIM7 */
+  
+  TIM2->CNT = 0x8000;
+  TIM2->ARR = 0xFFFF;
+  TIM2->SMCR |= TIM_SMCR_SMS_0; /* encoder mode 1 */
+  TIM2->CCMR1 |= TIM_CCMR1_CC1S_0|TIM_CCMR1_IC1F|TIM_CCMR1_CC2S_0|TIM_CCMR1_IC2F;  /* TI1, TI2 - inputs with max filter */
+  TIM2->CCER |= TIM_CCER_CC1E|TIM_CCER_CC2E; /* Enable inputs */
+  TIM2->SR = 0;
+  TIM2->DIER |= TIM_DIER_CC2IE/*|TIM_DIER_CC1IE*/; /* Interrupt by trigger */
+  TIM2->CR1 |= TIM_CR1_CEN;
+#endif
+  EventInit();
+   
+  do
+  	EventCheck();
+  while(1);
+}
+#endif  /* EVENT_DEMO*/
+

+ 61 - 0
src/event.h

@@ -0,0 +1,61 @@
+#if !defined(_EVENT_H_)
+#define _EVENT_H_
+
+#include <stm32f10x.h>
+
+#define EV_KEY_TOUCH    0x0100
+#define EV_KEY_PRESSED  0x0200 
+#define EV_KEY_LONG     0x0300
+#define EV_KEY_REPEATE  0x0400
+#define EV_KEY_REALIZED 0x0500
+#define EV_KEY_DOUBLE   0x0600
+#define EV_FUNC_FIRST   0x0700
+#define EV_MASK         0x0700
+
+extern uint16_t Event;  /* 0xKKKKKAAA */
+/* AAA - it is event key. Event can be defined by & operation with EV_MASK*/
+/* KKKKKK - keys - one bit fpr one key. Up to 5 keys */
+volatile extern uint16_t EventQueue; /* for setting user event */
+extern uint16_t EncCounter; /* 0 - 4096 */
+extern uint16_t EncStep;
+extern uint16_t EncStepMax;
+extern uint16_t EncMax;
+
+#define ENC_MAX_DEFAULT 4095
+#define ENC_STEP_MAX_DEFAULT 200;
+
+extern volatile uint8_t EvCounter; // Press delay counter
+typedef void (*MenuFunction_t)(void);
+extern MenuFunction_t CurrentFunction;  // Current function
+#define CurrentFunc(MenuFunc) CurrentFunction = MenuFunc
+
+extern void EventInit(void);
+extern void EventKeys(void); /* This function is periodicaly called (e.g. from ISR) */
+extern void EventCheck(void); /* This function should be called to handele the event */
+
+/* Event detect delays */
+#define KEY_PRESSED_VALUE 4 // Press event delay
+#define KEY_LONG_VALUE    140  // Long press event delay
+#define KEY_REPEATE_VALUE 160 // Repeate event delay
+#define KEY_REALIZE_VALUE 8 //  Realize event detect delay
+//#define KEY_DOUBLE_VALUE  40 //  Double event detect delay. It should be more then REALIZE_KEY_VALUE and KEY_PRESSED_VALUE
+
+#define KEYPORT GPIOA->IDR
+
+#define KEY1  GPIO_IDR_IDR2
+#define KEY2  GPIO_IDR_IDR1
+#define KEY3  GPIO_IDR_IDR0
+#define KEY4  GPIO_IDR_IDR3
+#define KEY5  GPIO_IDR_IDR5
+#define KEY6  GPIO_IDR_IDR6
+#define KEY_MASK_SYS (KEY1)
+#define KEY_MASK (KEY1|KEY2|KEY3|KEY4|KEY5)
+
+#define KEY_ENTER 	KEY1    /* Encoder button */
+#define KEY_DOWN 		KEY2    /* Encoder down */
+#define KEY_UP 	    KEY3    /* Encored event up */
+#define KEY_ADC     KEY4    /* Adc ready */
+#define KEY_CLOCK   KEY5    /* Second event */
+//#define KEY_GRAPH   KEY6    /* Gpath update */
+
+#endif /* _EVENT_H_ */

+ 390 - 0
src/graph.c

@@ -0,0 +1,390 @@
+#if defined(GRAPH)
+#include "LabArm.h"
+
+
+//static const Graph_t NullGraph = {32000,-32000};
+
+typedef struct
+{
+  int16_t Delta;
+  int16_t Intercept;
+  int16_t Round0;
+  int16_t Round1;
+} GraphTable_t;
+
+const GraphTable_t GraphTable [] = {
+{30,1,10,5},
+{60,2,20,10},
+{150,5,50,20},
+{300,10,100,50},
+{600,20,200,100},
+{1500,50,500,200},
+{3000,100,1000,500},
+{6000,200,2000,1000},
+{12000,400,4000,2000},
+{24000,800,8000,4000},
+};
+#define GRAPH_TABLE_SIZE (sizeof(GraphTable)/sizeof(GraphTable_t))
+
+Graph_t IGraphArray[GRAPH_SIZE];
+Graph_t VGraphArray[GRAPH_SIZE];
+uint8_t GraphCurrentPoint;
+GraphData_t GraphData;
+int16_t GlobalMin;
+int16_t GlobalMax;
+uint8_t GlobalMinPoint;
+uint8_t GlobalMaxPoint;
+uint8_t GlobalDiapason;
+
+void CalculateExtr(int16_t Min, int16_t Max, int16_t* pRoundMin, uint8_t* pDiapason)
+{
+  int16_t Delta;
+  int16_t Round;
+  uint8_t Diapason;
+  int16_t RoundMin;
+  int16_t RoundMax;
+  int16_t RoundDelta;
+
+  Delta = Max - Min;
+  
+  for (Diapason = 0; Diapason<GRAPH_TABLE_SIZE;Diapason++)
+  {
+    if ( Delta > GraphTable[Diapason].Delta )
+      continue;
+    break;
+  }
+
+ReselectDiapason:  
+  RoundDelta = GraphTable[Diapason].Delta;
+  Round = GraphTable[Diapason].Round0;
+
+  do
+  {
+    RoundMin = Min/Round*Round;
+    RoundMax = (Max+Round+1)/Round*Round;
+    if ( (RoundMax - RoundMin) <= RoundDelta )
+      break;
+    if (Round != GraphTable[Diapason].Round1)
+    {
+      Round = GraphTable[Diapason].Round1;
+      continue;
+    }
+    Diapason++;
+    goto ReselectDiapason;
+  }
+  while(1);
+  
+  {
+    int16_t Add = (RoundDelta - (RoundMax - RoundMin))/2;
+    RoundMin = RoundMin - Add;
+    RoundMax = RoundMax + Add;
+    if (RoundMin < 0 )
+    {
+      RoundMax = RoundMax - RoundMin;
+      RoundMin = 0;
+    }
+  }
+  
+  Round = GraphTable[Diapason].Round0;
+  do
+  {
+    RoundMin = RoundMin/Round*Round;
+    RoundMax = RoundMin + GraphTable[Diapason].Delta;
+    if ( RoundMax >= Max && RoundMin <= Min )
+      break;
+    if (Round != GraphTable[Diapason].Round1)
+    {
+      Round = GraphTable[Diapason].Round1;
+      continue;
+    }
+    Diapason++;
+    goto ReselectDiapason;
+  } while(1);
+
+  *pRoundMin = RoundMin;
+  *pDiapason = Diapason;
+}
+
+static int8_t Offsetmin(Graph_t* Array, int8_t ColumnNum)
+{
+  return (Array[ColumnNum].Min - GlobalMin)/GraphTable[GlobalDiapason].Intercept+1;
+}
+static int8_t Offsetmax(Graph_t* Array, int8_t ColumnNum)
+{
+  return (Array[ColumnNum].Max - GlobalMin)/GraphTable[GlobalDiapason].Intercept+1;
+}
+
+void OutOneColumn(Graph_t* Array, int8_t ColumnNum)
+{
+  int8_t OffsetMin,OffsetMax;
+  uint32_t Column = 0;
+  int8_t PrevMin;
+  int8_t PrevMax;
+  int8_t  Prev;
+  int8_t  Next;
+
+  Next = GraphCurrentPoint + 1;
+  if ( Next >= GRAPH_SIZE)
+    Next = 0;
+
+  if( ColumnNum == Next)
+  {
+    Column = 0xFFFFFFFF;
+    goto draw;
+  }
+
+  if ( ColumnNum%10 == 0)
+  {
+    Column = (1<<1)+(1<<3)+(1<<5)+(1<<7)+(1<<9)+(1<<11)+
+            (1<<13)+(1<<15)+(1<<17)+(1<<19)+(1<<21)+(1<<23)+(1<<25)+(1<<27)+(1<<29)+(1u<<31);
+  }
+  if ( ColumnNum%2 == 0)
+    Column |= (1<<1)+(1<<11)+(1<<21)+(1u<<31);
+  
+  Prev = ColumnNum - 1;
+  if (Prev < 0)
+    Prev = GRAPH_SIZE - 1;
+
+  
+  if ( Array[ColumnNum].Min != 32000)
+  {
+    int8_t Delta;
+
+    OffsetMin = Offsetmin(Array, ColumnNum);
+    OffsetMax = Offsetmax(Array, ColumnNum);
+    if ( Array[Prev].Min != 32000)
+    {
+      PrevMin = Offsetmin(Array, Prev);
+      PrevMax = Offsetmax(Array, Prev);
+    }
+    else
+    {
+      PrevMin = OffsetMin;
+      PrevMax = OffsetMax;
+    }
+    
+    Delta = OffsetMax - PrevMax - 1;
+    while (Delta > 0)
+    {
+      Column |= (1<<(OffsetMax-Delta));
+      Delta--;
+    }
+
+    Delta = PrevMin - OffsetMin - 1;
+    while (Delta > 0)
+    {
+      Column |= (1<<(PrevMin-Delta));
+      Delta--;
+    }
+
+    Delta = PrevMax - OffsetMax - 1;
+    while (Delta > 0)
+    {
+      Column |= (1<<(PrevMax-Delta));
+      Delta--;
+    }
+
+    Delta = OffsetMin - PrevMin - 1;
+    while (Delta > 0)
+    {
+      Column |= (1<<(OffsetMin-Delta));
+      Delta--;
+    }
+
+    Column |= (1<<OffsetMin);
+    Column |= (1<<OffsetMax);
+  }
+
+draw:
+  Column = __RBIT(Column);
+  LcdGotoXY(ColumnNum, 5);
+  LcdSend(Column>>24, LCD_DATA);
+  LcdGotoXY(ColumnNum, 4);
+  LcdSend(Column>>16, LCD_DATA);
+  LcdGotoXY(ColumnNum, 3);
+  LcdSend(Column>>8, LCD_DATA);
+  LcdGotoXY(ColumnNum, 2);
+  LcdSend(Column, LCD_DATA);
+}
+
+void PrintGraph()
+{
+  Graph_t* Array = GraphData.GraphArray;
+  int i;
+  uint8_t Diapason;
+  int16_t Min=32000;
+  int16_t Max=-32000;
+
+  /* Finding extremoum */
+  for(i=0; i<GRAPH_SIZE;i++)
+  {
+    if (Array[i].Max > Max )
+    {
+      Max = Array[i].Max;
+      GlobalMaxPoint = i;
+    }
+    if(Array[i].Min < Min)
+    {
+      Min = Array[i].Min;
+      GlobalMinPoint = i;
+    }
+  }
+  CalculateExtr(Min, Max, &Min, &Diapason);
+  Max = Min + GraphTable[Diapason].Delta;
+  if ( GlobalDiapason == Diapason && Min == GlobalMin && Max == GlobalMax)
+  {
+    int8_t Next = GraphCurrentPoint + 1;
+    if ( Next >= GRAPH_SIZE )
+      Next = 0;
+      
+    OutOneColumn(GraphData.GraphArray, Next);
+    OutOneColumn(GraphData.GraphArray, GraphCurrentPoint);
+    return;
+  }
+  GlobalMin = Min;
+  GlobalMax = Max;
+  GlobalDiapason = Diapason;
+  for ( i = 0; i<GRAPH_SIZE; i++)
+  {
+    OutOneColumn(Array, i);
+  }
+  OutValueSmall(1, 0, GlobalMin, GraphData.DotPosition, 0);
+  OutValueSmall(1, 7, GraphTable[GlobalDiapason].Delta/3, GraphData.DotPosition, 0);
+}
+
+static void SetupGraph(void);
+void DisplayGraph(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    GlobalDiapason = 0xFF;
+    PrintGraph();
+    return;
+  }
+  if ( (Event& EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_ENTER:
+        CurrentFunc(StartFunction);
+        return;
+      case KEY_DOWN:
+      case KEY_UP:
+        CurrentFunc(SetupGraph);
+        return;
+      case KEY_ADC:
+      {
+        int16_t ValueMin;
+        int16_t ValueMax;
+        uint8_t Next;
+
+
+        ValueMin = GraphData.GraphArray[GraphCurrentPoint].Min;
+        ValueMax = GraphData.GraphArray[GraphCurrentPoint].Max;
+        if (ValueMin < GlobalMin || ValueMax > GlobalMax || /* Larger diapason */
+            GraphCurrentPoint == GlobalMaxPoint || GraphCurrentPoint == GlobalMinPoint) /* Check Diapason */
+        {
+          PrintGraph();
+          return;
+        }
+
+        Next = GraphCurrentPoint + 1;
+        if ( Next >= GRAPH_SIZE)
+          Next = 0;
+
+        OutOneColumn(GraphData.GraphArray, GraphCurrentPoint);
+        OutOneColumn(GraphData.GraphArray, Next);
+
+        return;
+      }
+    }
+  }
+  return;
+}
+
+uint16_t TimeInterval = 5; /* 1 second */
+
+static uint8_t IntervalCorrection = 0;
+static void SetupGraph(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = 0;
+    IntervalCorrection = 0;
+    EncCounter = TimeInterval - 1;
+    goto redraw;
+  }
+  if ( (Event& EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        if (IntervalCorrection == 0)
+        {
+          MenuPos++;
+          if ( MenuPos > 2 )
+            MenuPos = 0;
+          goto redraw;
+        }
+      case KEY_DOWN:
+        if (IntervalCorrection == 0)
+        {
+          if ( MenuPos == 0 )
+            MenuPos = 2;
+          else
+            MenuPos--;
+          goto redraw;
+        }
+        TimeInterval = EncCounter + 1;
+        goto redraw;
+
+      case KEY_ENTER:
+        switch (MenuPos)
+        {
+          case 1: /* Clear */
+            ClearGraph();
+          case 0: /* Return */
+            CurrentFunc(DisplayGraph);
+            return;
+          default: /* Interval */
+            if ( IntervalCorrection == 0)
+              IntervalCorrection = 1;
+            else
+            {
+              IntervalCorrection = 0;
+            }
+        }
+    }
+  }      
+  return;
+
+redraw:  
+  LcdChr(14+1*Y_POSITION+0*X_POSITION + (MenuPos==0?INVERSE:0), "Return");
+  LcdChr(14+2*Y_POSITION+0*X_POSITION + (MenuPos==1?INVERSE:0), "Clear");
+  LcdChr(14+3*Y_POSITION+0*X_POSITION + (MenuPos==2?INVERSE:0), "Interval");
+  OutValueSmall(3, 9, TimeInterval*2, 3, MenuPos==2?1:0);
+}
+
+void ClearGraph(void)
+{
+  int i;
+
+  for (i = 0; i < GRAPH_SIZE; i++)
+  {
+    IGraphArray[i].Min = VGraphArray[i].Min = 32000;
+    IGraphArray[i].Max = VGraphArray[i].Max = -32000;
+  }
+
+  GraphCurrentPoint = 0;
+  IGraphArray[0].Max = 
+      IGraphArray[0].Min = HumanI;
+  VGraphArray[0].Max = 
+      VGraphArray[0].Min = HumanV;
+    
+  GlobalMin = 32000;
+  GlobalMax = -32000;
+}
+
+#endif

+ 406 - 0
src/n3310.c

@@ -0,0 +1,406 @@
+#include "n3310.h"
+
+/*--------------------------------------------------------------------------------------------------
+                                Private function prototypes
+--------------------------------------------------------------------------------------------------*/
+//  Function prototypes are mandatory otherwise the compiler generates unreliable code.
+
+const uint8_t FontLookup [] =
+{
+     0x00, 0x00, 0x00, 0x00, 0x00 ,  // sp
+     0x00, 0x00, 0x2f, 0x00, 0x00 ,   // !
+     0x00, 0x07, 0x00, 0x07, 0x00 ,   // "
+     0x14, 0x7f, 0x14, 0x7f, 0x14 ,   // #
+     0x24, 0x2a, 0x7f, 0x2a, 0x12 ,   // $
+     0xc4, 0xc8, 0x10, 0x26, 0x46 ,   // %
+     0x36, 0x49, 0x55, 0x22, 0x50 ,   // &
+     0x00, 0x05, 0x03, 0x00, 0x00 ,   // '
+     0x00, 0x1c, 0x22, 0x41, 0x00 ,   // (
+     0x00, 0x41, 0x22, 0x1c, 0x00 ,   // )
+     0x14, 0x08, 0x3E, 0x08, 0x14 ,   // *
+     0x08, 0x08, 0x3E, 0x08, 0x08 ,   // +
+     0x00, 0x00, 0x50, 0x30, 0x00 ,   // ,
+     0x10, 0x10, 0x10, 0x10, 0x10 ,   // -
+     0x00, 0x60, 0x60, 0x00, 0x00 ,   // .
+     0x20, 0x10, 0x08, 0x04, 0x02 ,   // /
+     0x3E, 0x51, 0x49, 0x45, 0x3E ,   // 0
+     0x00, 0x42, 0x7F, 0x40, 0x00 ,   // 1
+     0x42, 0x61, 0x51, 0x49, 0x46 ,   // 2
+     0x21, 0x41, 0x45, 0x4B, 0x31 ,   // 3
+     0x18, 0x14, 0x12, 0x7F, 0x10 ,   // 4
+     0x27, 0x45, 0x45, 0x45, 0x39 ,   // 5
+     0x3C, 0x4A, 0x49, 0x49, 0x30 ,   // 6
+     0x01, 0x71, 0x09, 0x05, 0x03 ,   // 7
+     0x36, 0x49, 0x49, 0x49, 0x36 ,   // 8
+     0x06, 0x49, 0x49, 0x29, 0x1E ,   // 9
+     0x00, 0x36, 0x36, 0x00, 0x00 ,   // :
+     0x00, 0x56, 0x36, 0x00, 0x00 ,   // ;
+     0x08, 0x14, 0x22, 0x41, 0x00 ,   // <
+     0x14, 0x14, 0x14, 0x14, 0x14 ,   // =
+     0x00, 0x41, 0x22, 0x14, 0x08 ,   // >
+     0x02, 0x01, 0x51, 0x09, 0x06 ,   // ?
+     0x32, 0x49, 0x59, 0x51, 0x3E ,   // @
+     0x7E, 0x11, 0x11, 0x11, 0x7E ,   // A
+     0x7F, 0x49, 0x49, 0x49, 0x36 ,   // B
+     0x3E, 0x41, 0x41, 0x41, 0x22 ,   // C
+     0x7F, 0x41, 0x41, 0x22, 0x1C ,   // D
+     0x7F, 0x49, 0x49, 0x49, 0x41 ,   // E
+     0x7F, 0x09, 0x09, 0x09, 0x01 ,   // F
+     0x3E, 0x41, 0x49, 0x49, 0x7A ,   // G
+     0x7F, 0x08, 0x08, 0x08, 0x7F ,   // H
+     0x00, 0x41, 0x7F, 0x41, 0x00 ,   // I
+     0x20, 0x40, 0x41, 0x3F, 0x01 ,   // J
+     0x7F, 0x08, 0x14, 0x22, 0x41 ,   // K
+     0x7F, 0x40, 0x40, 0x40, 0x40 ,   // L
+     0x7F, 0x02, 0x0C, 0x02, 0x7F ,   // M
+     0x7F, 0x04, 0x08, 0x10, 0x7F ,   // N
+     0x3E, 0x41, 0x41, 0x41, 0x3E ,   // O
+     0x7F, 0x09, 0x09, 0x09, 0x06 ,   // P
+     0x3E, 0x41, 0x51, 0x21, 0x5E ,   // Q
+     0x7F, 0x09, 0x19, 0x29, 0x46 ,   // R
+     0x46, 0x49, 0x49, 0x49, 0x31 ,   // S
+     0x01, 0x01, 0x7F, 0x01, 0x01 ,   // T
+     0x3F, 0x40, 0x40, 0x40, 0x3F ,   // U
+     0x1F, 0x20, 0x40, 0x20, 0x1F ,   // V
+     0x3F, 0x40, 0x38, 0x40, 0x3F ,   // W
+     0x63, 0x14, 0x08, 0x14, 0x63 ,   // X
+     0x07, 0x08, 0x70, 0x08, 0x07 ,   // Y
+     0x61, 0x51, 0x49, 0x45, 0x43 ,   // Z
+     0x00, 0x7F, 0x41, 0x41, 0x00 ,   // [
+     0x55, 0x2A, 0x55, 0x2A, 0x55 ,   // 55
+     0x00, 0x41, 0x41, 0x7F, 0x00 ,   // ]
+     0x04, 0x02, 0x01, 0x02, 0x04 ,   // ^
+     0x40, 0x40, 0x40, 0x40, 0x40 ,   // _
+     0x00, 0x01, 0x02, 0x04, 0x00 ,   // '
+     0x20, 0x54, 0x54, 0x54, 0x78 ,   // a
+     0x7F, 0x48, 0x44, 0x44, 0x38 ,   // b
+     0x38, 0x44, 0x44, 0x44, 0x20 ,   // c
+     0x38, 0x44, 0x44, 0x48, 0x7F ,   // d
+     0x38, 0x54, 0x54, 0x54, 0x18 ,   // e
+     0x08, 0x7E, 0x09, 0x01, 0x02 ,   // f
+     0x0C, 0x52, 0x52, 0x52, 0x3E ,   // g
+     0x7F, 0x08, 0x04, 0x04, 0x78 ,   // h
+     0x00, 0x44, 0x7D, 0x40, 0x00 ,   // i
+     0x20, 0x40, 0x44, 0x3D, 0x00 ,   // j
+     0x7F, 0x10, 0x28, 0x44, 0x00 ,   // k
+     0x00, 0x41, 0x7F, 0x40, 0x00 ,   // l
+     0x7C, 0x04, 0x18, 0x04, 0x78 ,   // m
+     0x7C, 0x08, 0x04, 0x04, 0x78 ,   // n
+     0x38, 0x44, 0x44, 0x44, 0x38 ,   // o
+     0x7C, 0x14, 0x14, 0x14, 0x08 ,   // p
+     0x08, 0x14, 0x14, 0x18, 0x7C ,   // q
+     0x7C, 0x08, 0x04, 0x04, 0x08 ,   // r
+     0x48, 0x54, 0x54, 0x54, 0x20 ,   // s
+     0x04, 0x3F, 0x44, 0x40, 0x20 ,   // t
+     0x3C, 0x40, 0x40, 0x20, 0x7C ,   // u
+     0x1C, 0x20, 0x40, 0x20, 0x1C ,   // v
+     0x3C, 0x40, 0x30, 0x40, 0x3C ,   // w
+     0x44, 0x28, 0x10, 0x28, 0x44 ,   // x
+     0x0C, 0x50, 0x50, 0x50, 0x3C ,   // y
+     0x44, 0x64, 0x54, 0x4C, 0x44     // z
+};
+
+
+
+/*--------------------------------------------------------------------------------------------------
+
+  Name         :  LcdInit
+
+  Description  :  Performs PINS & LCD controller initialization.
+
+  Argument(s)  :  None.
+
+  Return value :  None.
+
+--------------------------------------------------------------------------------------------------*/
+void LcdInit ( uint8_t Contrast )
+{
+  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; /* Port B Clock */
+
+  GPIOB->CRH &= ~(((GPIO_CRL_MODE0|GPIO_CRL_CNF0)<<(LCD_DC_PIN-8)*4) |
+                  ((GPIO_CRL_MODE0|GPIO_CRL_CNF0)<<(LCD_IN_PIN-8)*4) |
+                  ((GPIO_CRL_MODE0|GPIO_CRL_CNF0)<<(LCD_CLK_PIN-8)*4)|
+                  ((GPIO_CRL_MODE0|GPIO_CRL_CNF0)<<(LCD_RST_PIN-8)*4) ); // Reset all pins
+
+
+  GPIOB->CRH |=  (((GPIO_CRL_MODE0_1)<<(LCD_DC_PIN-8)*4) |
+                  ((GPIO_CRL_MODE0_1)<<(LCD_IN_PIN-8)*4) |
+                  ((GPIO_CRL_MODE0_1)<<(LCD_CLK_PIN-8)*4)|
+                  ((GPIO_CRL_MODE0_1)<<(LCD_RST_PIN-8)*4) ); // Pins in push-pull 10MHz
+    
+  GPIOB->BRR = 1<<LCD_RST_PIN; /* RESET to 0 */
+  if (Contrast == 0)
+    Contrast = 60;
+  if (Contrast > 90)
+    Contrast = 90;
+  /* Some delay */
+  {
+    __IO uint32_t Counter;
+    for ( Counter = 0; Counter <10; Counter++)
+      ; /* Blank */
+  }
+  GPIOB->BSRR = 1<<LCD_RST_PIN; /* RESET to 1 */
+#define FUNCTION_SET 0x20
+#define FUNCTION_PD  0x04
+#define FUNCTION_V   0x02
+#define FUNCTION_EXT 0x01
+  LcdSend( FUNCTION_SET|FUNCTION_EXT, LCD_CMD );  // LCD Extended Commands.
+#define SET_VOP      0x80
+  LcdSend( SET_VOP|Contrast, LCD_CMD );  // Set LCD Vop (Contrast). 3.06+0.06*72=4.38V
+#define TEMPERATURE_COEFFICIENT 0x4
+  LcdSend( TEMPERATURE_COEFFICIENT | 2, LCD_CMD );  // Set Temp coefficent. 2 (0-3)
+#define SET_BIAS 0x10
+#define BIAS_1_10 0x07
+#define BIAS_1_18 0x06
+#define BIAS_1_24 0x05
+#define BIAS_1_40 0x04
+#define BIAS_1_48 0x03
+#define BIAS_1_65 0x02
+#define BIAS_1_80 0x01
+#define BIAS_1_100 0x00
+  LcdSend( SET_BIAS|BIAS_1_48, LCD_CMD );  // LCD bias mode 1:48.
+
+  LcdSend( FUNCTION_SET, LCD_CMD );  // LCD Standard Commands, Horizontal addressing mode.
+#define DISPLAY_CONTROL 0x08
+#define DISPLAY_BLANK   0x00
+#define DISPLAY_NORMAL  0x04
+#define DISPLAY_ALLON   0x01
+#define DISPLAY_INVERSE 0x05
+  LcdSend( DISPLAY_CONTROL|DISPLAY_NORMAL, LCD_CMD );  // LCD in normal mode.
+
+#define SET_Y 0x40 /* from 0 to 5 */
+#define SET_X 0x80 /* from 0 to 83 */
+//  LcdClear();
+}
+
+//Clear display
+void LcdClear()
+{
+	uint16_t j = 7;
+  
+	while ( j-- )
+	{
+      uint16_t i = 84;
+
+      LcdSend(SET_Y|j, LCD_CMD ); 
+      LcdSend(SET_X|0, LCD_CMD);
+      while ( i-- )
+      {
+        LcdSend(0/*xff*/, LCD_DATA);
+      }  
+	}
+}
+
+/*--------------------------------------------------------------------------------------------------
+
+  Name         :  LcdContrast
+
+  Description  :  Set display contrast.
+
+  Argument(s)  :  contrast -> Contrast value from 0x00 to 0x7F.
+
+  Return value :  None.
+
+  Notes        :  No change visible at ambient temperature.
+
+--------------------------------------------------------------------------------------------------*/
+void LcdContrast ( uint8_t Contrast )
+{
+    //  LCD Extended Commands.
+  LcdSend( FUNCTION_SET|FUNCTION_EXT, LCD_CMD );  // LCD Extended Commands.
+
+    // Set LCD Vop (Contrast).
+    if ( Contrast > 90 ) /* Limit by 8.5 V */
+      Contrast = 90;
+  LcdSend( SET_VOP | Contrast, LCD_CMD );
+
+    //  LCD Standard Commands, horizontal addressing mode.
+  LcdSend( FUNCTION_SET, LCD_CMD );
+}
+
+void LcdGotoXY(uint8_t X, uint8_t Y)
+{
+    LcdSend( SET_X|X, LCD_CMD );
+    LcdSend( SET_Y|Y, LCD_CMD ); 
+}
+
+
+/*--------------------------------------------------------------------------------------------------
+
+  Name         :  LcdChr
+
+  Description  :  Displays a character at current cursor location and increment cursor location.
+
+  Argument(s)  :  Ctrl = X*X_MUL+y*Y_MUL+ BIG1 + BIG2 + OutLen
+                  NoInverse == 0 - inverse output
+                  Str - out string
+
+  Return value :  None.
+
+--------------------------------------------------------------------------------------------------*/
+
+void LcdChr ( uint32_t Ctrl, const char* Str )
+{
+  unsigned char ch;
+  uint8_t Inverse;
+#if defined(BIG)
+#define INTERNAL_BIG_DOWN
+  uint32_t Big = Ctrl;
+#endif  
+  Inverse = ((Ctrl & INVERSE) != 0);
+  LcdGotoXY((Ctrl/X_POSITION & 0x0F)*6 + ((Ctrl/X_OFFSET)& 0x3F), 
+            (Ctrl/Y_POSITION) & 0x07);
+
+  Ctrl = Ctrl & 0x7F; /* Only Len */
+  while ( Ctrl != 0 )
+  {
+    const uint8_t* Start;
+
+    if ( (ch = *Str) != 0)
+      Str++;
+    Ctrl--;
+    if ( ch == 0 ) 
+      ch = ' ';
+    if ( (ch < 0x20) || (ch > 0x7b) )
+    {
+        //  Convert to a printable character.
+        ch = 92;
+    }
+    ch = ch - 32;
+
+    Start = &FontLookup[ch*4+ch];
+
+#if defined(BIG)
+    if ( (Big & (BIG_UP|BIG_DOWN)) == 0 )
+#endif
+    { 
+      int i;
+      for (i=0; i<=4; i++)
+      {
+        uint8_t E = Start[i];
+        if ( Inverse )
+        {
+          E = ~E;
+        }
+        LcdSend( E, LCD_DATA );
+      }
+      if ( !Inverse )
+        LcdSend( 0, LCD_DATA );
+      else
+        LcdSend( 0xFF, LCD_DATA );
+    }
+#if defined(BIG)
+    else
+    {
+      int i;
+      for (i=0; i<=4; i++)
+      {
+        uint8_t Element = 0;
+        uint8_t E = Start[i];
+
+        if ( Big & BIG_UP )
+        {
+          if(E & 0x01) Element |= 0x03;
+          if(E & 0x02) Element |= 0x0C;
+          if(E & 0x04) Element |= 0x30;
+          if(E & 0x08) Element |= 0xC0;
+        }
+        else
+        {
+          if(E & 0x10) Element |= 0x03;
+          if(E & 0x20) Element |= 0x0C;
+          if(E & 0x40) Element |= 0x30;
+          if(E & 0x80) Element |= 0xC0;
+        }
+        if (Inverse)
+          Element = ~Element;
+        LcdSend( Element, LCD_DATA );
+        LcdSend( Element, LCD_DATA );
+      } /* For */
+      if ( !Inverse )
+      {
+        LcdSend( 0, LCD_DATA );
+        LcdSend( 0, LCD_DATA );
+      }
+      else
+      {
+        LcdSend( 0xFF, LCD_DATA );
+        LcdSend( 0xFF, LCD_DATA );
+      }
+    }
+#endif /* BIG */
+  }
+}
+
+
+/*--------------------------------------------------------------------------------------------------
+
+  Name         :  LcdSend
+
+  Description  :  Sends data to display controller.
+
+  Argument(s)  :  data -> Data to be sent
+                  cd   -> Command or data (see/use enum)
+
+  Return value :  None.
+
+--------------------------------------------------------------------------------------------------*/
+void LcdSend ( uint8_t Data, LcdCmdData cd )
+{
+  int i;
+
+  if ( cd != LCD_CMD ) /* If data */
+  {
+    GPIOB->BSRR = 1<<LCD_DC_PIN; /* DC pin to hight */
+  }
+  else
+  {
+    GPIOB->BRR = 1<<LCD_DC_PIN; /* DC pin to low */
+  }
+  LCD_DELAY;  
+  i=8;
+  do
+  {
+    if ( Data & 0x80 )
+      GPIOB->BSRR = 1<<LCD_IN_PIN; /* IN pin to hight */
+    else
+      GPIOB->BRR  = 1<<LCD_IN_PIN; /* IN pin to low */
+	LCD_DELAY;
+    Data = Data<<1; /* Some additional delay */
+    GPIOB->BSRR  = 1<<LCD_CLK_PIN; /* CLK pin to hight */
+	LCD_DELAY;
+	i--;
+    GPIOB->BRR  = 1<<LCD_CLK_PIN; /* CLK pin to low */
+  }while(i);
+}
+
+/*--------------------------------------------------------------------------------------------------
+                                     Character generator
+
+             This table defines the standard ASCII characters in a 5x7 dot format.
+--------------------------------------------------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------------------------------------------
+                                         End of file.
+--------------------------------------------------------------------------------------------------*/
+
+
+
+#if defined(LCDDEBUG)
+void SystemInit()
+{
+}
+
+int main(void)
+{
+  RCC->CFGR |= (RCC_CFGR_HPRE_1|RCC_CFGR_HPRE_3); /* div 8 */
+  LcdInit();
+  LcdClear();
+  LcdChr ( Y_POSITION*1+X_POSITION*1+13, "Hello world" );
+  LcdChr ( Y_POSITION*2+X_POSITION*1+13+INVERSE+X_OFFSET*3, "Hello world" );
+  LcdChr ( Y_POSITION*4+X_POSITION*0+2+BIG_UP, "15" );  
+  LcdChr ( Y_POSITION*5+X_POSITION*0+2+BIG_DOWN, "15" );  
+  return 0;
+}
+#endif

+ 62 - 0
src/n3310.h

@@ -0,0 +1,62 @@
+#if !defined(_N3310_H_)
+#define _N3310_H_
+#include "stm32f10x.h"
+/*--------------------------------------------------------------------------------------------------
+                                  General purpose constants
+--------------------------------------------------------------------------------------------------*/
+#define LCD_DC_PIN                 12
+#define LCD_IN_PIN                 13
+#define LCD_CLK_PIN                14
+#define LCD_RST_PIN                15
+
+#define LCD_DELAY { int i=16; while(i) {__NOP(); i--;}; }
+typedef enum
+{
+    LCD_CMD  = 0,
+    LCD_DATA = 1
+} LcdCmdData;
+void LcdSend    ( uint8_t data, LcdCmdData cd );
+
+/*--------------------------------------------------------------------------------------------------
+                                 Public function prototypes
+--------------------------------------------------------------------------------------------------*/
+void LcdInit       ( uint8_t Contrast );
+#define X_POSITION 0x0100
+#define Y_POSITION 0x1000
+#define BIG_UP   0x8000 
+#define BIG_DOWN 0x0080
+#define INVERSE  0x80000000
+#define X_OFFSET 0x00010000
+/*--------------------------------------------------------------------------------------------------
+
+  Name         :  LcdChr
+  Argument(s)  :  Ctrl = X*X_MUL+y*Y_MUL+ BIG1 + BIG2 + OutLen
+                  Inverse == 1 - inverse output
+                  Str - out string
+ Description  :  Displays a character at current cursor location and increment cursor location.
+              Len from 0 to 84
+              y from 0 to 5
+              x from 0 to 13
+              BIG_UP -  upper part of 16 point string
+              BIG_DOWN -lower part of 16 point string 
+              BIG_UP   | Y2 | Y1 | Y0 | X3 | X2 | X1 | X0
+              BIG_DOWN | L6 | L5 | L4 | L3 | L2 | L1 | L0
+              It is parts of Ctrl. Ctrl = X_MUL*X+Y_MUL*Y+LENGTH+(BIG_UP||BIG_DOWN||0)
+              Length should be >= strlen(Str). 
+              Example:LcdChr(5*X_MUL+3*Y_MUL+8, 1, "123") - will out string 123 in position 6 row 4 8 points font.
+              The 5 symbols afrer 123 will be cleaned ( 8 - strlen("123"))
+
+  Return value :  None.
+
+--------------------------------------------------------------------------------------------------*/
+void LcdChr ( uint32_t Ctrl, const char* Str );
+/*  LcdChr ( Y_POSITION*2+X_POSITION*1+13+INVERSE+X_OFFSET*3, "Hello world" );*/
+
+void LcdClear(void);
+void LcdContrast ( uint8_t Contrast );
+void LcdGotoXY(uint8_t X, uint8_t Y);
+
+#endif  //  _N3310_H_
+/*--------------------------------------------------------------------------------------------------
+                                         End of file.
+--------------------------------------------------------------------------------------------------*/

+ 801 - 0
src/ui.c

@@ -0,0 +1,801 @@
+#include <string.h>
+#include "LabArm.h"
+
+Config_t CurrentConfig;
+Settings_t CurrentSettings;
+#if defined(RAW)
+uint8_t DisplayRaw;
+#endif
+uint8_t MenuPos;
+uint16_t Flags;
+
+void ApplyOut(void);
+void PredefinedMenu(void);
+void Configure(void);
+
+
+static UserMenu_t* Menu;
+
+static CancelButton(void)
+{
+  if (!(Flags & (AUTO_APPLY_FLAG|IMEDDIATE_APPLY_FLAG)) && /* New values are set but not applied */
+       (DAC->DOR1 != 0 && DAC->DOR2 !=0 )) /* Only if the output is enabled 05.10.11 */
+  {
+    CurrentSettings.VoltageDAC = DAC->DOR1;
+    CurrentSettings.CurrentDAC = DAC->DOR2;
+  }
+  CurrentFunc(StartFunction);
+  return;
+}
+
+void HandleUserMenu(void)
+{
+  int NewValue;
+  SubConfig_t* SubConfig;
+
+  if ( Menu->IVFlag == MENU_V_FLAG)
+    SubConfig = &CurrentConfig.V;
+  else
+    SubConfig = &CurrentConfig.I;
+
+  NewValue = (Menu->Value - SubConfig->DACOffset)/ SubConfig->DACRamp;
+  if ( NewValue < 0 )
+    NewValue = 0;
+  if ( NewValue > 4095 )
+    NewValue = 4095;
+  if ( Menu->IVFlag == MENU_V_FLAG)
+    CurrentSettings.VoltageDAC = NewValue;
+  else
+    CurrentSettings.CurrentDAC = NewValue;
+  if ( Flags & (AUTO_APPLY_FLAG|IMEDDIATE_APPLY_FLAG) )
+    ApplyOut();
+  else
+    CurrentFunc(StartFunction);
+}
+
+#define MENU_0       0
+#define MENU_1       1
+#define MENU_2       2
+#define MENU_3       3
+#define MENU_4       4
+#define MENU_5       5
+#define MENU_6       6
+#define MENU_CNCL_RET 7
+#define MENU_7       8
+#define MENU_8       9
+#define MENU_9       10
+#define MENU_10      11
+#define MENU_11      12
+#define MENU_12      13
+#define MENU_13      14
+
+static void DisplayMenu(int IsSetMenu)
+{
+  int i;
+  char* Text;
+
+  for (i=MENU_0; i<=MENU_13;i++)
+  {
+    int x = i/5 * 5;
+    int y = i%5 + 1;
+    int TextPos = (i< MENU_7)?i:i-1;
+    Text = CurrentConfig.Menu[TextPos].Text;
+    LcdChr(X_POSITION*x+Y_POSITION*y+4 + (i==MenuPos)*INVERSE, Text);
+  }
+
+  if (!IsSetMenu)
+    Text = "Cncl";
+  else
+    Text = "Retu";
+  LcdChr(X_POSITION*5+Y_POSITION*3+4 + (MENU_CNCL_RET==MenuPos)*INVERSE, Text);
+}
+
+void ConfigureTheMenu(void);
+
+void UConfigureTheMenu()
+{
+  char* Text;
+  SubConfig_t* Config;
+
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = 0;
+
+    if (Menu->IVFlag != MENU_I_FLAG) /* It can be polluted */
+      Menu->IVFlag = MENU_V_FLAG;
+    else
+      Menu->IVFlag = MENU_I_FLAG;
+
+    if (Menu->Text[0] == 0)
+      Menu->Text[0] = '0';
+    if (Menu->Text[1] == 0)
+      Menu->Text[1] = '1';
+    if (Menu->Text[2] == 0)
+      Menu->Text[2] = '2';
+    if (Menu->Text[3] == 0)
+      Menu->Text[3] = '3';
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+      case KEY_DOWN:
+        switch(MenuPos)
+        {
+          case 0:
+            if (Menu->IVFlag & MENU_I_FLAG)
+              Menu->IVFlag = MENU_V_FLAG;
+            else
+              Menu->IVFlag = MENU_I_FLAG;
+            goto redraw;
+          case 1:
+            if (EncCounter > 999 )
+              EncCounter = 999;
+            Menu->Value = EncCounter*10 + 1; /* it's better to see changes from 9001 to 9000 that 9000 to 8999 */
+            goto redraw;
+          default:
+            if (EncCounter > 'z')
+              EncCounter = 'z';
+            Menu->Text[MenuPos-2] = EncCounter;
+            goto redraw;
+        }
+      case KEY_ENTER:
+        switch(MenuPos)
+        {
+          case 0:
+            if (Menu->IVFlag & MENU_V_FLAG)
+              Menu->IVFlag = MENU_V_FLAG;
+            else
+              Menu->IVFlag = MENU_I_FLAG;
+            EncCounter = Menu->Value;
+            break;
+          default:
+            EncCounter = Menu->Text[MenuPos - 1];
+        }
+        MenuPos++;
+        if (MenuPos == 6)
+          MenuPos = 0;
+        goto redraw;
+    }
+  }
+  if ( (Event & EV_MASK) == EV_KEY_LONG &&
+       (Event & KEY_MASK) == KEY_ENTER )
+  {
+    CurrentFunc(ConfigureTheMenu);
+  }
+  return;
+
+redraw:
+  if (Menu->IVFlag != MENU_I_FLAG) /* Voltage */
+  {
+    Text = " Voltage";
+    Config = &CurrentConfig.V; 
+  }
+  else
+  {
+    Text = " Current";
+    Config = &CurrentConfig.I; 
+  }
+  LcdChr(X_POSITION*0+Y_POSITION*1+14 + (0==MenuPos)*INVERSE, Text);
+  OutValueSmall(3, 3, Menu->Value, Config->DotPosition, MenuPos == 1);
+  LcdChr(X_POSITION*2+Y_POSITION*5+1 + (2==MenuPos)*INVERSE, &Menu->Text[0]);
+  LcdChr(X_POSITION*3+Y_POSITION*5+1 + (3==MenuPos)*INVERSE, &Menu->Text[1]);
+  LcdChr(X_POSITION*4+Y_POSITION*5+1 + (4==MenuPos)*INVERSE, &Menu->Text[2]);
+  LcdChr(X_POSITION*5+Y_POSITION*5+1 + (5==MenuPos)*INVERSE, &Menu->Text[3]);
+}
+
+
+void YesNoCalib(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = 0;
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+      case KEY_DOWN:
+        if(MenuPos == 0)
+          MenuPos++;
+        else
+          MenuPos = 0;
+        goto redraw;
+      case KEY_ENTER:
+        if(MenuPos)
+          CurrentFunc(CalibrationMenu);
+        else
+          CurrentFunc(Configure);
+    }
+  }
+  return;
+redraw:
+  LcdChr(X_POSITION*0+Y_POSITION*1+14, "Do you want");
+  LcdChr(X_POSITION*0+Y_POSITION*2+14, "to start the");
+  LcdChr(X_POSITION*0+Y_POSITION*3+14, "calibration");
+  LcdChr(X_POSITION*0+Y_POSITION*5+7 + (0==MenuPos)*INVERSE,  "No");
+  LcdChr(X_POSITION*7+Y_POSITION*5+7 + (0!=MenuPos)*INVERSE,  "Yes");
+}
+
+#define  CONF_MENU_EDIT 0
+#define  CONF_CALIBR    1
+#define  CONF_BACKUP    2
+#define  CONF_CLOCK     3
+#define  CONF_CONTRAST  4
+
+void Configure(void)
+{
+ if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = CONF_CONTRAST;
+    goto redraw;
+  } 
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+       case KEY_DOWN:
+        if ( MenuPos == 0 )
+          MenuPos = 4;
+        else
+          MenuPos = MenuPos - 1;
+        goto redraw;
+      case KEY_UP:
+        if ( MenuPos == 4 )
+          MenuPos = 0;
+        else
+          MenuPos = MenuPos + 1;
+        goto redraw;
+      case KEY_ENTER:
+        switch(MenuPos)
+        {
+          case CONF_MENU_EDIT: /* Menu set up */
+            CurrentFunc(ConfigureTheMenu);
+            return;
+          case CONF_CALIBR: /* Calibration */
+            CurrentFunc(YesNoCalib);
+            return;
+          case CONF_BACKUP: /* Save to backup */
+            if ( Flags&BACKUP_SAVE_FLAG )
+              Flags &= ~BACKUP_SAVE_FLAG;
+            else
+              Flags |= BACKUP_SAVE_FLAG;
+            goto redraw;
+          case CONF_CLOCK: /* Time */
+#if defined(CLOCK_ENABLED)
+            if (IS_ON_CLOCK)
+            {
+              CurrentFunc(SetupTheClock);
+              return;
+            }
+            else
+            {
+              SwitchOnTheClock();
+            }
+#endif
+            goto redraw;
+          case CONF_CONTRAST:
+            AfterContrast = StartFunction;
+            CurrentFunc(ContrastMenu);
+        }
+    }
+  }
+  return;   
+
+redraw:
+  LcdChr (Y_POSITION*1+X_POSITION*0+14+(CONF_MENU_EDIT==MenuPos)*INVERSE, "Menu Edit" );
+  LcdChr (Y_POSITION*2+X_POSITION*0+14+(CONF_CALIBR==MenuPos)*INVERSE, "Calibration" );
+  LcdChr (Y_POSITION*3+X_POSITION*1+14+(CONF_BACKUP==MenuPos)*INVERSE, "Save Backup");
+  LcdChr (Y_POSITION*3+X_POSITION*0+1+ (CONF_BACKUP==MenuPos)*INVERSE, Flags&BACKUP_SAVE_FLAG?"+":"-" );
+  LcdChr (Y_POSITION*4+X_POSITION*0+14+(CONF_CLOCK==MenuPos)*INVERSE, 
+#if defined(CLOCK_ENABLED)
+                              IS_ON_CLOCK?"Setup time":"Time on" 
+#else
+                              "Reserved"
+#endif  
+                                                       );
+  LcdChr (Y_POSITION*5+X_POSITION*0+14+(CONF_CONTRAST==MenuPos)*INVERSE, "Contrast" );
+
+}
+
+void ConfigureTheMenu(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = MENU_CNCL_RET;
+    goto redraw;
+  } 
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_DOWN:
+        if ( MenuPos == 0 )
+          MenuPos = 14;
+        else
+          MenuPos = MenuPos - 1;
+        goto redraw;
+      case KEY_UP:
+        if ( MenuPos == 14 )
+          MenuPos = 0;
+        else
+          MenuPos = MenuPos + 1;
+        goto redraw;
+      case KEY_ENTER:
+        switch(MenuPos)
+        {
+          case MENU_0:
+          case MENU_1:
+          case MENU_2:
+          case MENU_3:
+          case MENU_4:
+          case MENU_5:
+          case MENU_6:
+            Menu=&CurrentConfig.Menu[MenuPos];
+            goto usermenu;
+          case MENU_7:
+          case MENU_8:
+          case MENU_9:
+          case MENU_10:
+          case MENU_11:
+          case MENU_12:
+          case MENU_13:
+            Menu=&CurrentConfig.Menu[MenuPos - 1];
+            goto usermenu;
+          default:
+            CurrentFunc(SaveMenu);
+            return;
+        } /* switch MenuPos*/
+    } /* switch key */
+  }
+  return;
+usermenu:
+  CurrentFunc(UConfigureTheMenu);
+
+  return;
+redraw:
+  DisplayMenu(1);
+}
+
+void PredefinedMenu(void)
+{
+ /* 1    6    10 */
+ /* 2    7    11  */
+ /* 3    cncl 12 */
+ /* 4    8    13  */
+ /* 5    9    14  */
+
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = MENU_CNCL_RET;
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_DOWN:
+        if ( MenuPos == 0 )
+          MenuPos = 14;
+        else
+          MenuPos = MenuPos - 1;
+        goto redraw;
+      case KEY_UP:
+        if ( MenuPos == 14 )
+          MenuPos = 0;
+        else
+          MenuPos = MenuPos + 1;
+        goto redraw;
+      case KEY_ENTER:
+        switch(MenuPos)
+        {
+          case MENU_CNCL_RET: /* Default cancel input*/
+            CancelButton();
+            return;
+          case MENU_0:
+          case MENU_1:
+          case MENU_2:
+          case MENU_3:
+          case MENU_4:
+          case MENU_5:
+          case MENU_6:
+            Menu=&CurrentConfig.Menu[MenuPos];
+            goto usermenu;
+          case MENU_7:
+          case MENU_8:
+          case MENU_9:
+          case MENU_10:
+          case MENU_11:
+          case MENU_12:
+          case MENU_13:
+            Menu=&CurrentConfig.Menu[MenuPos-1];
+            goto usermenu;
+        } /* switch MenuPos*/
+    } /* switch key */
+  }
+  return;
+usermenu:
+  if ( (Menu->IVFlag != MENU_I_FLAG) &&
+     (Menu->IVFlag != MENU_V_FLAG))  /* Do nothing - invalid menu */
+    return;
+  CurrentFunc(HandleUserMenu);
+  return;
+redraw:
+  DisplayMenu(0);
+}
+
+
+void ApplyOut(void)
+{
+  int Delta;
+
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    goto redraw;
+  }
+  if ( CurrentSettings.VoltageDAC == DAC->DOR1 &&
+          CurrentSettings.CurrentDAC == DAC->DOR2 )
+  {
+     CurrentFunc(StartFunction);
+     return;
+  }
+
+  Delta = (int)CurrentSettings.VoltageDAC - (int)(DAC->DOR1);
+  if ( Delta > 50 )
+    Delta = 50;
+  if ( Delta < -50)
+    Delta = -50;
+  DAC_V = (int)(DAC->DOR1) + Delta;
+
+  Delta = (int)CurrentSettings.CurrentDAC - (int)(DAC->DOR2);
+  if ( Delta > 50 )
+    Delta = 50;
+  if ( Delta < -50)
+    Delta = -50;
+  DAC_I = (int)(DAC->DOR2) + Delta;
+
+  OutValueSmall(2, 9, HumanV, CurrentConfig.V.DotPosition, 0);
+  OutValueSmall(5, 9, HumanI, CurrentConfig.I.DotPosition, 0);
+  return;
+
+redraw:
+  LcdChr(Y_POSITION*1+X_POSITION*0+14, " Applying...");
+  OutValue(2, 0, VoltageFromDac(), CurrentConfig.V.DotPosition, 0xFF);
+  OutValue(4, 0, VoltageFromDac(), CurrentConfig.V.DotPosition, 0xFF);
+}
+
+uint32_t OriginalValue;
+void SetNewCurrent(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    LcdChr ( Y_POSITION*1+X_POSITION*0+14, "New current" );
+
+    EncCounter = CurrentSettings.CurrentDAC;
+    OriginalValue = CurrentSettings.CurrentDAC;
+    goto redraw;
+  } 
+
+  if ( (Event& EV_MASK) != EV_KEY_PRESSED )
+    return;
+
+  switch (Event & KEY_MASK)
+  {
+    case KEY_UP:
+    case KEY_DOWN:
+      CurrentSettings.CurrentDAC = EncCounter;
+	  if (Flags&IMEDDIATE_APPLY_FLAG)
+	  DAC_I	= EncCounter;
+      goto redraw;
+    case KEY_ENTER:
+      if ( CurrentSettings.CurrentDAC != OriginalValue ) /* Current is changed */
+      {
+          if ( (Flags&(AUTO_APPLY_FLAG|IMEDDIATE_APPLY_FLAG)) )
+            CurrentFunc(ApplyOut);
+          else
+            CurrentFunc(StartFunction);
+      }
+      else
+        CurrentFunc(StartFunction);
+  }
+  return;
+redraw:
+  OutValue(3,2, CurrentFromDac(), CurrentConfig.I.DotPosition, 0xFF);
+  OutValueSmall(5, 0, HumanI, CurrentConfig.I.DotPosition, 0);
+}
+
+void SetNewVoltage(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    LcdChr ( Y_POSITION*1+X_POSITION*0+14, "New voltage" );
+
+    EncCounter = CurrentSettings.VoltageDAC;
+    OriginalValue = CurrentSettings.VoltageDAC;
+    goto redraw;
+  } 
+
+  if ( (Event& EV_MASK) != EV_KEY_PRESSED )
+    return;
+
+  switch (Event & KEY_MASK)
+  {
+    case KEY_UP:
+    case KEY_DOWN:
+      CurrentSettings.VoltageDAC = EncCounter;
+	  if (Flags&IMEDDIATE_APPLY_FLAG)
+	  DAC_V	= EncCounter;
+      goto redraw;
+    case KEY_ENTER:
+      if ( CurrentSettings.VoltageDAC != OriginalValue ) /* Voltage is changed */
+      {
+          if ( (Flags&(AUTO_APPLY_FLAG|IMEDDIATE_APPLY_FLAG)) )
+            CurrentFunc(ApplyOut);
+          else
+            CurrentFunc(StartFunction);
+      }
+      else
+        CurrentFunc(SetNewCurrent);
+  }
+  return;
+redraw:
+  OutValue(3,2, VoltageFromDac(), CurrentConfig.V.DotPosition, 0xFF);
+  OutValueSmall(5, 0, HumanV, CurrentConfig.V.DotPosition, 0);
+}
+
+void SmallSettings(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = 2; /* Cancel */
+    goto redraw;
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_UP:
+        MenuPos++;
+        if ( MenuPos > 5 )
+          MenuPos = 0;
+        goto redraw;
+      case KEY_DOWN:
+        if ( MenuPos == 0 )
+          MenuPos = 5;
+        else
+          MenuPos--;
+        goto redraw;
+      case KEY_ENTER:
+        switch(MenuPos)
+        {
+          case 0: /* Configuration */
+            CurrentFunc(Configure);
+            return;
+          case 1:
+            if (Flags&AUTO_APPLY_FLAG)
+            {
+              Flags &= ~AUTO_APPLY_FLAG;
+              Flags |= IMEDDIATE_APPLY_FLAG;
+            }
+            else if (Flags & IMEDDIATE_APPLY_FLAG)
+            {
+              Flags &= ~(AUTO_APPLY_FLAG|IMEDDIATE_APPLY_FLAG);
+            }
+            else
+            {
+              Flags |= AUTO_APPLY_FLAG;
+            }
+            goto redraw;
+          case 2: /* Cancel */
+            CancelButton();
+            return;
+          case 3: /* Current graph */
+#if defined(GRAPH)
+            GraphData.GraphArray = IGraphArray;
+            GraphData.DotPosition = CurrentConfig.I.DotPosition;
+            CurrentFunc(DisplayGraph);
+#endif
+            return;
+          case 4: /* Voltage graph */
+#if defined(GRAPH)
+            GraphData.GraphArray = VGraphArray;
+            GraphData.DotPosition = CurrentConfig.V.DotPosition;
+            CurrentFunc(DisplayGraph);
+            return;
+#endif
+          case 5: /* Timer set up */
+#if defined(CLOCK_ENABLED)
+            if (IS_ON_CLOCK)
+              CurrentFunc(TimerSetup);
+#endif
+            return;
+
+        } /* switch MenuPos*/
+    } /* switch key */
+  }
+  return;
+redraw:
+  LcdChr(14+1*Y_POSITION+0*X_POSITION + (MenuPos==0?INVERSE:0), "Settings");
+  LcdChr(
+#if !defined(CLOCK_ENABLED)
+  14
+#else
+  5
+#endif  
+     + 2*Y_POSITION+0*X_POSITION + (MenuPos==1?INVERSE:0), 
+    ( Flags & AUTO_APPLY_FLAG )?
+	      "Auto":
+	  ( (Flags & IMEDDIATE_APPLY_FLAG)? "Immd":"Manu"));
+  LcdChr(14+3*Y_POSITION+0*X_POSITION + (MenuPos==2?INVERSE:0), "Cancel");
+#if defined(GRAPH)
+  LcdChr(14+4*Y_POSITION+0*X_POSITION + (MenuPos==3?INVERSE:0), "I Graph");
+  LcdChr(14+5*Y_POSITION+0*X_POSITION + (MenuPos==4?INVERSE:0), "V Graph");
+#else
+  LcdChr(14+4*Y_POSITION+0*X_POSITION + (MenuPos==3?INVERSE:0), "Reserved");
+  LcdChr(14+5*Y_POSITION+0*X_POSITION + (MenuPos==4?INVERSE:0), "Reserved");
+#endif
+#if defined(CLOCK_ENABLED)
+  LcdChr(8+2*Y_POSITION+6*X_POSITION + (MenuPos==5?INVERSE:0), (IS_ON_CLOCK)? "Timer":"");
+#endif
+}
+
+#if defined(CLOCK_ENABLED)
+static void ApplyWithDelay(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+  }
+
+  if ( (Event& EV_MASK) == EV_KEY_PRESSED  && 
+       (Event & KEY_MASK) == KEY_CLOCK )
+  {
+    RemainTimerValue = TimerValue;
+    CurrentFunc(ApplyOut);
+  }
+}
+#endif
+
+void StartFunction(void)
+{
+  if (Event == EV_FUNC_FIRST)
+  {
+    LcdBlank(); /* Clear screen */
+    MenuPos = 0; /* There are no key ENTER pressed */
+    goto redraw;
+  }
+
+  if ( (Event& EV_MASK) == EV_KEY_PRESSED )
+  {
+    switch (Event & KEY_MASK)
+    {
+      case KEY_DOWN:
+        CurrentFunc(SmallSettings);
+        return;
+      case KEY_UP:
+        CurrentFunc(PredefinedMenu);
+        return;
+      case KEY_ADC:
+        goto redraw;
+#if defined(CLOCK_ENABLED)
+      case KEY_CLOCK:
+      {
+        if ( TimerValue != 0 && RemainTimerValue != 0xFFFF )
+        {
+          DisplayClock(RemainTimerValue, 10+0x8000, Y_POSITION*3+X_POSITION*6);
+        }
+        else
+        {
+          LcdChr(Y_POSITION*3+X_POSITION*6+3, "" );
+          DisplayClock((RTC->CNTH<<16) + RTC->CNTL, 10, Y_POSITION*3+X_POSITION*9);
+        }
+        return;
+      }
+#endif        
+      case KEY_ENTER:/* Reserve current */
+      {
+        MenuPos = 1; /* Key ENTER was pressed */
+        goto redraw;
+      }
+    }
+  }
+
+  if ( (Event & EV_MASK) == EV_KEY_LONG ) 
+  {
+    if ( CurrentSettings.VoltageDAC == DAC->DOR1 &&
+         CurrentSettings.CurrentDAC == DAC->DOR2 )
+    {
+      /* Long key set OFF output */
+      DAC_V = 0;
+      DAC_I = 0;
+      MenuPos = 0; /* It is long press. The event was handled. No need do anything on key realized */
+#if defined(CLOCK_ENABLED)
+      RemainTimerValue = 0;
+#endif
+      return;
+    }
+
+    /* Apply seved or set voltage and current */
+    MenuPos = 0;
+#if defined(CLOCK_ENABLED)
+    if (TimerValue != 0)
+    {
+      CurrentFunc(ApplyWithDelay);
+    }
+    else
+#endif
+    {
+      CurrentFunc(ApplyOut);
+    }
+    return;   
+  }
+  
+
+  if ( (Event& EV_MASK) == EV_KEY_REALIZED ) /* realised event current */
+  {
+    if (MenuPos) /* No long press */
+      CurrentFunc(SetNewVoltage);
+    return;
+  }
+
+  return;
+  
+redraw:
+#if defined(RAW)
+  if ( DisplayRaw == 0)
+#endif
+  {
+    OutValue(1, 0, HumanV, CurrentConfig.V.DotPosition, 0xFF);
+    OutValue(4, 0, HumanI, CurrentConfig.I.DotPosition, 0xFF);
+    OutValueSmall(1, 9, VoltageFromDac(), 
+                  CurrentConfig.V.DotPosition, CurrentSettings.VoltageDAC != DAC->DOR1);
+    OutValueSmall(5, 9, CurrentFromDac(), 
+                  CurrentConfig.I.DotPosition, CurrentSettings.CurrentDAC != DAC->DOR2);
+  }
+#if defined(RAW)
+  else
+  {
+    OutValue(1, 0, ADCVoltage/(128*16), 4, 0xFF);
+    OutValue(4, 0, ADCCurrent/(128*16), 4, 0xFF);
+    OutValueSmall(1, 9, CurrentSettings.VoltageDAC, 4, CurrentSettings.VoltageDAC != DAC->DOR1);
+    OutValueSmall(5, 9, CurrentSettings.CurrentDAC, 4, CurrentSettings.CurrentDAC != DAC->DOR2);
+  }
+#endif
+}
+
+int16_t VoltageFromAdc(void)
+{
+  return ADCVoltage*CurrentConfig.V.ADCRamp + CurrentConfig.V.ADCOffset;
+}
+
+int16_t CurrentFromAdc(void)
+{
+  return ADCCurrent*CurrentConfig.I.ADCRamp + CurrentConfig.I.ADCOffset;
+}
+
+int16_t CurrentFromDac()
+{
+  return CurrentSettings.CurrentDAC*CurrentConfig.I.DACRamp + CurrentConfig.I.DACOffset;
+}
+int16_t VoltageFromDac()
+{
+  return CurrentSettings.VoltageDAC*CurrentConfig.V.DACRamp + CurrentConfig.V.DACOffset;
+}