/* -------------------------------------------------------
                         versuche_int.c

     Versuche zum STM8.

     zur Fehlersuche ohne jegliche Einbindung eines
     Softwaremoduls oder einer Headerdatei.

     Hier  : Timer- und Interruptprogrammierung

     MCU   :  STM8S103F3

     28.06.2016  R. Seelig
   ------------------------------------------------------ */

// Schreibweisen meiner Gewohnheit geschuldet
#define uint8_t  unsigned char
#define uint16_t unsigned int


// CLOCK - Register
#define CLK_ICKR        *(uint8_t*)0x50C0
#define CLK_ECKR        *(uint8_t*)0x50C1
#define CLK_SWR         *(uint8_t*)0x50C4
#define CLK_SWCR        *(uint8_t*)0x50C5
#define CLK_CKDIVR      *(uint8_t*)0x50C6
#define CLK_PCKENR1     *(uint8_t*)0x50C7
#define CLK_CCOR        *(uint8_t*)0x50C9
#define CLK_PCKENR2     *(uint8_t*)0x50CA
#define CLK_HSITRIMR    *(uint8_t*)0x50CC
#define CLK_SWIMCCR     *(uint8_t*)0x50CD

// CLOCK - Bitadressen
#define HSIEN           (1 << 0)
#define HSIRDY          (1 << 1)
#define SWEN            (1 << 1)
#define HSEEN           (1 << 0)
#define HSERDY          (1 << 1)
#define SWIF            (1 << 3)
#define SWBSY           (1 << 3)

// Timer1
#define TIM1_CR1        *(uint8_t*)0x5250
#define TIM1_SR1        *(uint8_t*)0x5255
#define TIM1_IER        *(uint8_t*)0x5254
#define TIM1_PSCRH      *(uint8_t*)0x5260
#define TIM1_PSCRL      *(uint8_t*)0x5261
#define TIM1_ARRH       *(uint8_t*)0x5262
#define TIM1_ARRL       *(uint8_t*)0x5263


// Timer1 - Bitadressen
#define TIM1_SR1_UIF    (1 << 0)
#define TIM1_CR1_CEN    (1 << 0)
#define TIM1_CR1_APRE   (1 << 7)
#define TIM1_IER_UIE    (1 << 0)


// Port-D
#define PD_ODR          *(uint8_t*)0x500F
#define PD_IDR          *(uint8_t*)0x5010
#define PD_DDR          *(uint8_t*)0x5011
#define PD_CR1          *(uint8_t*)0x5012
#define PD_CR2          *(uint8_t*)0x5013

//   PD4 als Ausgang
#define PD4_output_init()  {  PD_DDR |=   0x10;         \
                              PD_CR1 |=   0x10;         \
                              PD_CR2 &= ~(0x10); }

#define PD4_set()          (  PD_ODR |=   0x10   )
#define PD4_clr()          (  PD_ODR &= ~(0x10)  )

/* ------------------------------------------------------
                     delay_ms
      ganz grobe Verzoegerungsschleife fuer 1ms bei
      16 MHz
   ------------------------------------------------------ */
void delay_ms(uint16_t cnt)
{
  volatile uint16_t cnt2;

  while (cnt)
  {
    cnt2= 1435;
    while(cnt2)
    {
      cnt2--;
    }
    cnt--;
  }
}


/* ------------------------------------------------------
                     sysclock_init
      stellt den Taktgeber fuer den Controller ein fuer
      Benutzung interner Oszillator / 16 MHz
   ------------------------------------------------------ */
void sysclock_init(void)
{
  CLK_ICKR = 0;                                  //  Reset Register interner clock
  CLK_ECKR = 0;                                  //  Reset Register externer clock (ext. clock disable)

  CLK_ICKR =  HSIEN;                             //  Interner clock enable
  while ((CLK_ICKR & (HSIRDY)) == 0);            //  warten bis int. Takt eingeschwungen ist


  CLK_CKDIVR = 0;                                //  Taktteiler auf volle Geschwindigkeit
  CLK_PCKENR1 = 0xff;                            //  alle Peripherietakte an
  CLK_PCKENR2 = 0xff;                            //  dto.

  CLK_CCOR = 0;                                  //  CCO aus
  CLK_HSITRIMR = 0;                              //  keine Taktjustierung
  CLK_SWIMCCR = 0;                               //  SWIM = clock / 2.
  CLK_SWR = 0xe1;                                //  int. Generator als Taktquelle
  CLK_SWCR = 0;                                  //  Reset clock switch control register.
  CLK_SWCR = SWEN;                               //  Enable switching.
  while ((CLK_SWCR &  SWBSY) != 0);              //  warten bis Peripherietakt stabil
  delay_ms(50);
}

/* ------------------------------------------------------
                     sysclock_xtal_init
      stellt den Takt fuer den STM8S auf externen
      Quarz ein
   ------------------------------------------------------ */
void sysclock_xtal_init(void)
{
  CLK_ICKR = 0;                                  //  Reset Register interner clock
  CLK_ECKR = HSEEN;

  while ((CLK_ECKR & HSERDY) == 0);              //  warten bis Quarz eingeschwungen ist;

  CLK_SWR = 0xb4;                                //  int. Generator als Taktquelle
//    while ((CLK_SWCR &  SWIF) != 0);               //  warten bis Takt stabil
  CLK_SWCR = SWEN;                               //  Enable switching.

  CLK_CKDIVR = 0;                                //  Taktteiler auf volle Geschwindigkeit
  CLK_PCKENR1 = 0xff;                            //  alle Peripherietakte an
  CLK_PCKENR2 = 0xff;                            //  dto.
  delay_ms(50);
}

/* ------------------------------------------------------
                     int_enable
      grundsaetzlich Interrupts zulassen
   ------------------------------------------------------ */
void int_enable(void)
{
  __asm;
    rim
  __endasm;
}


/* ------------------------------------------------------
                     int_disable
      grundsaetzlich Interrupts sperren
   ------------------------------------------------------ */
void int_disable(void)
{
  __asm;
    sim
  __endasm;
}


/* --------------------------------------------------
                       tim1_ovf
      Interruptvector fuer Timer1 overflow (11 ist
      die Vector-Adresse)
   -------------------------------------------------- */
void tim1_ovf(void) __interrupt(11)
{
  static volatile char freq_flag = 0;

  if (freq_flag)
  {
    PD4_set();
    freq_flag= 0;
  }
  else
  {
    PD4_clr();
    freq_flag= 1;
  }

  TIM1_SR1 &= ~TIM1_SR1_UIF;        // Interrupt quittieren
}

/* --------------------------------------------------
                       tim1_init
      intialisiert Timer mit Taktteiler 1/16 F_CPU
      und Reloadwert 1000 (0x3e8)

      16 MHz / 16 = 1 MHz => 1 uS

      Reloadwert = 1000  => Interrupt sollte
                         1000 * 1us = 1ms aufgerufen
                         werden
   -------------------------------------------------- */
void tim1_init(void)
{

  // Prescaler ( Taktteiler ) / 10
  // Timer wird somit bei F_CPU= 16 MHz mit 1uS getaktet
  TIM1_PSCRH= 0;
  TIM1_PSCRL= 16;


  // Enable overflow
  TIM1_IER |= TIM1_IER_UIE;

  // Timer1 starten
  TIM1_CR1 |= TIM1_CR1_CEN;     // Timer1 enable ( CEN = ClockENable )
  TIM1_CR1 |= TIM1_CR1_APRE;    // Timer1 autoreload ( mit Werten in TIM1_ARR )

  // Reloadwert soll 1000 sein = 0x03e8
  TIM1_ARRH= 0x03;
  TIM1_ARRL= 0xe8;

  int_enable();                 // Interrupts grundsaetzlich zulassen
}


/* ###################################################
                         M-A-I-N
   ################################################### */
int main(void)
{

  sysclock_init();                     // initialisieren mit internem RC
//  sysclock_xtal_init();                // externer Quarz
  PD4_output_init();
  tim1_init();

  // Endlosschleife, togglen von PD4 geschieht im Timerinterrupt
  while(1);
}
