Inconsistent variable period pulses in CTC mode

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm trying to output unmodulated IR pulses on an ATtiny85.

 

So far I'm having trouble correctly forming pulses about half the time.

For example, the following diagram shows the desired and unwanted output when trying to output the waveform for the 8 bit value 0x0F.

The signal is triggered when hitting a button, a few attempts are usually enough to get both signals to occur:

 

 

This is my current code:

 

    void setupPCM () {
    TCCR0B = 1<<CS01;  // prescaler 8
    TCCR0A = 1<<COM0B0 |1<<WGM01; // CTC Mode, toggle OC0B on compare match
    OCR0A=0xFF;
}

void stopPCM () {
    TCCR0A &= ~(1<<WGM01);
    TCCR0A &= ~(1<<COM0B0);
}

void pulse (int high, int low) {
    OCR0A=high;
    for (char i=0; i<2; i++) {
        //wait for flag
        do ; while ((TIFR & 1<<OCF0A) == 0);
        TIFR = 1<<OCF0A;
        OCR0A = low;
    }
}

void sendCode (uint8_t code) {
    TCNT0=0;
    for (int Bit=7; Bit>-1; Bit--) {
        if (code & ((unsigned long) 1<<Bit)) pulse(82, 184); else pulse(80, 50);
    }
}

void transmit(){
    setupPCM ()
    sendCode(0x0F);
    stopPCM();
}

int main() {
        sei();
    while(1){
            //wait for button press then call transmit()    

    }
}

 

I suspect the way I'm doing it has the output toggle from a variable internal state depending on the moment the button is hit.

 

How do I correctly time the start of the signal, or in general prevent the malformed output from occuring ? 

This topic has a solution.
Last Edited: Thu. Sep 21, 2017 - 12:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mr-maurice wrote:

 

I suspect the way I'm doing it has the output toggle from a variable internal state depending on the moment the button is hit.

 

How do I correctly time the start of the signal, or in general prevent the malformed output from occuring ? 

 

Rather than toggle OC0B on compare match, update your code to to Set/Clear OC0B as appropriate for High and Low period.

 

To guarantee the state of OC0B when starting a code transmission, update setupPCM to set/clear OC0B to the state you want:

; Initialize Timer State

; Set OC0B State
; For example, to initialize OC0B to low
TCCR0A = (1<<COM0B1) | (1<<WGM01); / CTC Mode, Clear OC0B on compare match
TCCR0B = (1<<CS01) | (1<<FOC0B) ; Prescalar 8, _force output compare B_ OC0B

; Set OCF0A State
TIFR = (1<<OCF0A) ; Clear OCF0A

 

See page 79 of the manual for caveats and details of force output compare.

 

Also, consider doing this

void stopPCM () {
    TCCR0A = 0; // Disable timer
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Why not use fast PWM mode instead, since it has well defined levels, instead o toggle and messing around with force output compare? Moreover, in PWM modes the OCR0A and B registers are buffered, so you can load the values for the next pulse at any time.

 

That is, set fast PWM with OCR0A as TOP in non-inverted mode, and changing the pin on match with OCR0B. Load OCR0A with low+high, and OCR0B with high. Update OCR0A and B with the next bit values on compare match with OCR0B. These will be buffered and auto-loaded on terminal count.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When in doubt, I use normal mode and toggle on compare match.

In the ISR, increment the match value.

International Theophysical Year seems to have been forgotten..
Anyone remember the song Jukebox Band?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SolarFreak wrote:

mr-maurice wrote:

 

I've edited my code to account for your recommendations, as well as made a few changes. Is it what you had in mind:

 

void setupPCM () {
	TCCR0B = (1<<CS01) | (1<<FOC0B);
	TCCR0A = (1<<COM0B1) |(1<<WGM01); //CTC Mode, Clear OC0B on compare match
	TIFR = (1<<OCF0A);
}

void stopPCM () {
	TCCR0A =0;
}

void pulse (int high, int low) {
	//high state
	TCCR0A = (1<<COM0B1)|(1<<COM0B0) ;//set on compare match
	OCR0A=high;
	do ; while ((TIFR & 1<<OCF0A) == 0);//wait for match
	TIFR = 1<<OCF0A;//clear compare match flag

	//low state
	TCCR0A &= ~ (1<<COM0B0) ;//COM0B0=0: clear on compare match
	OCR0A=low;
	do ; while ((TIFR & 1<<OCF0A) == 0);//wait for match
	TIFR = 1<<OCF0A;//clear compare match flag

}

void sendCode (uint16_t code) {
	TCNT0=0;
	for (int Bit=15; Bit>-1; Bit--) {
		if (code & (1<<Bit)) pulse(184, 82); else pulse(50, 80);
	}
}

void transmit(uint8_t address,uint8_t code){
	setupPCM();
	sendCode(code+(address<<8));
	stopPCM();
}

?

I must have gotten something wrong because only a few pulses go out, with a longer duration so as to occupy the length of a normal message.

This is what I get with a 16 bit message for example:

30 ms is approximately the length of a standard burst.

 

El Tangas wrote:

Why not use fast PWM mode instead, since it has well defined levels, instead o toggle and messing around with force output compare? Moreover, in PWM modes the OCR0A and B registers are buffered, so you can load the values for the next pulse at any time.

 

That is, set fast PWM with OCR0A as TOP in non-inverted mode, and changing the pin on match with OCR0B. Load OCR0A with low+high, and OCR0B with high. Update OCR0A and B with the next bit values on compare match with OCR0B. These will be buffered and auto-loaded on terminal count.

 

To be honest at a glance it looked like the CTC mode was appropriate for variable period signals so I went this way.

I haven't experimented with your method yet but I have a few questions:

  • does it mean using the dedicated interrupt routine to handle updating OCR0A and OCR0B ? (using global variables ?)
  • "changing the pin on match with OCR0B": does it mean changing TCCR0A:COM0B0 and COM0B1 on the fly depending on the state I need the pin in ? (Page 78, table 11-3 in the  datasheet).
Last Edited: Mon. Sep 18, 2017 - 08:10 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wouldn't it be simplier to use the bit banging?

 

if(pin == 1)

{

   output = 1;

   delay_us(100);

   output = 0;

   delay_us(50);

}

else

{

   output = 1;

   delay_us(50);

   output = 0;

   delay_us(50);

}

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mr-maurice wrote:

 

To be honest at a glance it looked like the CTC mode was appropriate for variable period signals so I went this way.

I haven't experimented with your method yet but I have a few questions:

  • does it mean using the dedicated interrupt routine to handle updating OCR0A and OCR0B ? (using global variables ?)
  • "changing the pin on match with OCR0B": does it mean changing TCCR0A:COM0B0 and COM0B1 on the fly depending on the state I need the pin in ? (Page 78, table 11-3 in the  datasheet).

 

Regarding your first point, yes; the interrupt routine (or polling the interrupt flag if you prefer to not use interrupts) will update OCR0A and OCR0B to reflect the next bit, or stop the timer if there are no more bits. The flag to check is TIFR0:OCF0B.

 

Regarding your second point, no; TCCR0A:COM0B0 and COM0B1 are just initialized to 0b10 to set up non-inverting mode, then you just leave them alone.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Visovian wrote:

Wouldn't it be simplier to use the bit banging?

 

if(pin == 1)

{

   output = 1;

   delay_us(100);

   output = 0;

   delay_us(50);

}

else

{

   output = 1;

   delay_us(50);

   output = 0;

   delay_us(50);

}

 

This is the first method I tried but it reliably produces an innacurately timed bit in the middle of a transmission near the 8th or 9th bit. It seemed less elegant than using an hardware feature too.

 

El Tangas wrote:

Regarding your first point, yes; the interrupt routine (or polling the interrupt flag if you prefer to not use interrupts) will update OCR0A and OCR0B to reflect the next bit, or stop the timer if there are no more bits. The flag to check is TIFR0:OCF0B.

 

Regarding your second point, no; TCCR0A:COM0B0 and COM0B1 are just initialized to 0b10 to set up non-inverting mode, then you just leave them alone.

Thanks, so in quick pseudocode that would be:

;set fast pwm non inverting on OC0B, TOP=OCRA ("mode 7")

;signal_bits: 16 bits wide bit array

//setting pulse lenghts duration depending on 'value' (1 or 0)
;OCR0A=signal_bits[0].value[low_length]+signal_bits[0].value[high_length]
;OCR0B=signal_bits[0].value[high_length]
;   do: poll TIFR:OCF0B
;   done

;for {value in signal_bits[1 to 15]} do:
;OCR0A=value[low_length]+value[high_length];
;OCR0B=value[high_length]
;   do: poll TIFR:OCF0B
;   done
;done

//disable pwm
;TCCR0A=0

 

Last Edited: Thu. Sep 21, 2017 - 09:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, I believe that should work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Yes, I believe that should work.

 

Thank you so much. I get clean regular pulses now (the first 2 bits at the beginning of a pulse train are "missing", which is probably unrelated to the pwm feature.)

I consider my question answered.

 

This is my current code if anyone's interested:

 

const int JVC_ADDRESS = 0x8F;

void setupPCM () {
	TCCR0A = (1<<WGM01)| (1<<WGM00)|(1<<COM0B1);
	TCCR0B = (1<<WGM02)|( 1<<CS01);
}

void stopPCM () {
	TCCR0A =0;
}

void sendCode (unsigned long code) {
	TCNT0=0;
	//edited to test with an 8 bit value, this should be 16
	for (int Bit=0; Bit<8; Bit++) {
		if (code & (unsigned long)(1<<Bit)) {
			OCR0A=255;
			OCR0B=80;
		}
		else {
			OCR0A=130;
			OCR0B=80;
		};
		do ; while ((TIFR & 1<<OCF0B) == 0);
		TIFR = 1<<OCF0B;
	}
}

void transmit(uint8_t address,uint8_t code){
	setupPCM();
	sendCode((unsigned long) JVC_ADDRESS);
	stopPCM();
}

 

2 long pulses should appear at the beginning:

Last Edited: Thu. Sep 21, 2017 - 01:00 PM