cancel
Showing results for 
Search instead for 
Did you mean: 

How to implement micros() function on stm8s003?

carlogulliani
Associate III

I need to decode ASK/OOK signal, for this aim I need to know the period in microseconds between several edges. I decided to use TIM1 for this purpose, here is my code

// Set fCPU = 16MHz
    CLK_CKDIVR = 0;
    CLK_PCKENR1 = 0xFF;
    CLK_PCKENR2 = 0;
 
uint16_t micros() {
    return (uint16_t) ((TIM1_CNTRH << 8) | TIM1_CNTRL);
}
 
void tim1_init(void) {
    // 1MHz = 1uS
    TIM1_PSCRH = 0x00;
    TIM1_PSCRL = 0x10;
 
    TIM1_IER = 0x00; // disable interrupts
    TIM1_CR1 = 0x01;
}

and to get signal I use

ISR_PORTD(handlerD) {
	state = PinRead(GD0_PORT, GD0_PIN);
  uint16_t m = micros();
  if (state == HIGH) {
    lolen = abs(m - prevtime);
  }
  else {
    hilen = abs(m - prevtime);
  }
  prevtime = abs(m);
  if (state == HIGH)
  {
    if (flag == 0) {
      // the end of packet
      if (CheckValue(Pe, hilen) && CheckValue(Pe2, lolen)) // valid 1
      {
        if (bcounter < 32)
          code1 = (code1 << 1) | 1;
        else if (bcounter < 64)
          code2 = (code2 << 1) | 1;
        bcounter++;
      }
      else if (CheckValue(Pe2, hilen) && CheckValue(Pe, lolen)) // valid 0
      {
        if (bcounter < 32)
          code1 = (code1 << 1) | 0;
        else if (bcounter < 64)
          code2 = (code2 << 1) | 0; bcounter++;
      }
      else
        bcounter = 0;
    }
    else {
      if (bcounter < 32)
          code1 = (code1 << 1) | 1;
        else if (bcounter < 64)
          code2 = (code2 << 1) | 1;
        bcounter++;
    }
  }
  //printf("bcounter: %d\r\n", bcounter);
  if (bcounter >= 65)
  {
    printf("Got: %x %x\r\n", code1, code2);
    Pe2 = lolen;
    Pe = hilen;
    flag = 1;
    bcounter = 0;
    code1 = 0;
    code2 = 0;
  }
}

but get the values not I expected

8 REPLIES 8
henry.dick
Senior II

"but get the values not I expected"

which are what?

help us help you.

carlogulliani
Associate III

Thanks for your reply!

If I set my timer for 1 MHz, I get STM8 is always rebooting. I also would like to use GPIO to catch my clock, but I can't use event trigger irq, cause 1uS is too fast for this one

CLK_CKDIVR = 0; // For using HSI = 16MHz
// 1MHz = 1uS
TIM1_PSCRH = 0x00;
TIM1_PSCRL = 0x10; // Prescaler 16 -> 16000000 / 16 = 1000000 = 1MHz = 1 uS
 
TIM1_IER = 0x01; // overflow interrupt or even TIM1_IER = 0x00;
TIM1_CR1 = 0x01; // start timer

Cristian Gyorgy
Senior III

Hello!

If you want to get the precise time of an edge of a signal, you went the wrong way. If you enable an external interrupt on a port and in the execution of the interrupt you "manually" read the timer you get errors due to interrupt servicing times and others...

You should activate an input capture interrupt for the timer you use and input your signal on the timer input pins. This way you will have errors <= timer period.

You can even use 2 channels, each on differernt edge (rising/falling), so this way you know exactly what edge you have in the interrupt, without needing to test the pin.

Thank you for your reply! Seems capture/compare are what I need, but could you help me to understand, how to set timer 2 for CC?

My radio is connected to PD3 and generates PWM signal with +- the similar period, but with different pulse width. If the pulse width < T/2, this is 0 and if PW > T/2 - 1. I think I need to use timer with CC, looks like PD3 is TIM2 CH2

I set the frequency of micro controller to 16MHz and the freq of timer to 1MHz(1 us)

CLK_CKDIVR = 0; // 16MHz
CLK_PCKENR1 = 0xFF; // Enable periphery
CLK_PCKENR2 = 0;

Next I init the timer, start it and listening events

ISR_TIM2_CC(TIM2_CAP_COM) {
  // how to count pulse width?
}
 
void tim2_DeInit(void)
{
  TIM2_CR1 = 0;
  TIM2_IER = 0;
  TIM2_SR1 = 0;
  TIM2_SR2 = 0;
  TIM2_EGR = 0;
  TIM2_CCMR1 = 0;
  TIM2_CCMR2 = 0;
  TIM2_CCMR3 = 0;
  TIM2_CCER1 = 0;
  TIM2_CCER2 = 0;
  TIM2_CNTRH = 0;
  TIM2_CNTRL = 0;
  TIM2_PSCR = 0;
  TIM2_ARRH = 0xFF;
  TIM2_ARRL = 0xFF;
  TIM2_CCR1H = 0;
  TIM2_CCR1L = 0;
  TIM2_CCR2H = 0;
  TIM2_CCR2L = 0;
  TIM2_CCR3H = 0;
  TIM2_CCR3L = 0;
}
 
void tim2_init(void) {
  tim2_DeInit();
 
  TIM2_PSCR = 0x04;// 16/2^4 = 1MHz
 
  // CC settings
  TIM2_CCMR2 = 0x01;
  TIM2_CCER2 = 0x20 | 0x11;
   
  // Enable interruption and start timer
  TIM2_IER |= (1 << 2); // enable channel 2 for CC
  TIM2_CR1 = TIM2_CR1_ARPE | TIM2_CR1_CEN;
}

I'm not sure that my CC settings are correct. It seems every event is triggered on every rising edge and I need to use some flag to count the pulse width, but how to count this PW? But I can't to set falling edge, or I need to reconfigure timer for falling edge capture when I got rising edge event.

It would be great if someone share the example of configure TIM2 ch2 for counting pulse width

Cristian Gyorgy
Senior III

So, I don't know your setup, but you say you want to use TIM2_CH2 input pin. You could setup TIM2 the following way:

0693W000004H2afQAC.png

(The image above is from RM0016)

Set inputs for channels 1 & 2:

TIM2_CCMR1 = 0x22 (N=4 samples filter, input for CH1=TI2FP1)

TIM2_CCMR2 = 0x21 (N=4 samples filter, input for CH2=TI2FP2)

now you need to set the active edge and enable capture:

TIM2_CCER1 = 0x31 (rising edge for CH1 and falling edge for CH2).

You still need to enable the capture interrupts and maybe also the update interrupt to keep track of time.

Every edge will capture TIM2 value, and then you have enough time to read the captured value. Simple math, by subtracting the time between 2 edges will give you the captured signal.

Good luck!

carlogulliani
Associate III

Thank you, it seems what I need:thumbs_up:

To get the capture value do I need to read CCR2H & CCR2L registers and clear the ire flag SR1 and SR2? And is there way to detect which edge generated interruption?

upd:

Set the timer like you offered, but my micro controller is always rebooting after that, and there is no any interruption

void tim2_init(void) {
    tim2_DeInit();
 
    TIM2_PSCR = 0x04; // 16/2^4 = 1MHz
 
    // set up capture compare
 
    //(N=4 samples filter, input for CH1=TI2FP1)
    TIM2_CCMR1 |= (1 << 1); // CC1 channel is configured as input, IC1 is mapped on TI2FP1
    TIM2_CCMR1 |= (1 << 5); // Input capture 1 filter, N = 4
    
    //(N=4 samples filter, input for CH2=TI2FP2)
    TIM2_CCMR2 |= (1 << 0); // CC2 channel is configured as input, IC2 is mapped on TI2FP2
    TIM2_CCMR2 |= (1 << 5); // Input capture 2 filter, N = 4
 
    TIM2_CCER1 |= (1 << 4); // Capture enable for CC2IE
    TIM2_CCER1 |= (1 << 0); // Capture enable for CC1IE
    
    // Enable interruption and start timer
    TIM2_IER |= (1 << 0);   // set bit CC2IE
    TIM2_IER |= (1 << 1);   // set bit CC1IE
    TIM2_IER |= (1 << 2);   // set bit UIE
 
    // start timer
    TIM2_CR1 = 0x01;
}

GPIO is set as input and I removed IO's interruption

PinInput(GD0_PORT,GD0_PIN);
	PinInputFloat(GD0_PORT,GD0_PIN);

Cristian Gyorgy
Senior III

You have separate interrupts on TIM2 for Capture and for Update. When the input capture channels catch an edge, the TIM2 capture/compare interrupt is triggered. Inside the interrupt, by reading TIM2_SR1 register you know which channel triggered teh interrupt: check CC1F/CC2F flags. After you know the edge you can read the captured value in TIM2_CCR1/2. The interrupt flag is reset by reading the value.

"Set the timer like you offered, but my micro controller is always rebooting after that, and there is no any interruption" - what are your interrupt handlers? Watchdog enabled? An unhandled interrupt will reset the MCU if there is no code at the int. vector or if the interrupt flag remains set and jumps continuously to the int. vector.

And any reason why you write like this:

    // Enable interruption and start timer
    TIM2_IER |= (1 << 0);   // set bit CC2IE
    TIM2_IER |= (1 << 1);   // set bit CC1IE
    TIM2_IER |= (1 << 2);   // set bit UIE

and not:

TIM2_IER = 0x22;

Still can't catch interruption, but checked this one with logic analyser and the signal is there. But I found out why I was getting reboot. There are bad contacts of my wires. But they don't affect on signal and timer, cause they are connected on pub via signal lines. My IRQ handler is

#define ISR_TIM2_CC(name) void name(void) __interrupt(14)
....
ISR_TIM2_CC(TIM2_CAP_COM) {
    if (TIM2_SR1 & 0x04) { // interrupt on rising edge
        if (!flag){
            flag = 1;
            pulse_width = 0;
            TIM2_SR1 = 0;
        }
        else {
            flag = 0;
            period = (((TIM2_CCR2H << 8) | TIM2_CCR2L) - period);
            if (period > 200) { // check if it's not a preamble
                code = (code << 1) | (pulse_width < period/2) ? 1 : 0;
                period = 0;
                TIM2_CCR2H = 0;
                TIM2_CCR2L = 0;
                counter++;
            }
            else {
                preamble = (preamble << 1) | 1;
            }
        }
        
        if (TIM2_SR2) { // overcapture
            TIM2_SR2 = 0;
        }
    }
    if (TIM2_SR1 & 0x02) { // interrupt on falling edge
        pulse_width = (TIM2_CCR2H << 8) | TIM2_CCR2L;
        if (counter >= 60) {
            printf("Preamble: 0x%x\r\n", preamble);
            printf("Code: 0x%x\r\n", code);
            counter = 0;
        }
    }
}

Even if I write something like that, I get nothing

#define ISR_TIM2_CC(name) void name(void) __interrupt(14)
...
ISR_TIM2_CC(TIM2_CAP_COM) {
     TIM2_SR1 = 0;
     TIM2_SR2 = 0;
     printf("irq\r\"n);
}

I'm used to write register value by bit shifting, it's easy to understand what are settings I set. BTW, TIM2_IER = 0x22 is not correct, cause bits 4:5 are reserved according to RM