Consecutive PWM signals with different prescaler settings

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

My ATtiny85 circuit generates signals like this one on PB1 (OC0B) using fast PWM (preamble, 16 bit data, stop pulse):

 

I have trouble with the leftmost longer pulse. At the moment i use simple _delay_us statements to generate it, but it sometimes introduces glitches later near the end of the PWM (orange and yellow) parts.

 

It seemed smart to switch to using timers for this part but I can't get it to work correctly.

This is my code:

const uint8_t JVC_MUTE = 0x71;
const uint8_t JVC_ADDRESS = 0xF1;

void setupPWM () {
	//enables fast pwm, non inverting,TOP=OCR0A, prescaler 8
	TCCR0A = (1<<WGM01)| (1<<WGM00)|(1<<COM0B1);
	TCCR0B = (1<<WGM02)|( 1<<CS01);
}

void stopPWM () {
	TCCR0A = 0;
}

/*emits a long preamble pulse
*/
void preamble(){
	//enables fast pwm, non inverting, prescaler 64
	TCCR0A = (1<<WGM01)| (1<<WGM00)|(1<<COM0B1);
	TCCR0B = (1<<WGM02)|( 1<<CS01)|(1<<CS00);
	TCNT0=0;
	OCR0A=255;
	OCR0B=80;
	do ; while ((TIFR & 1<<OCF0B) == 0);
	TIFR = 1<<OCF0B;
	TCCR0A = 0;
}

/*emits 16 bit of data using fast PWM
code:16 bit data value to be send (address + command)
*/
void sendCode (uint16_t code) {
	TCNT0=0;

        //padding instructions 1
	OCR0A=0;
	OCR0B=0;
	do ; while ((TIFR & 1<<OCF0B) == 0);
	TIFR = 1<<OCF0B;
        //padding instructions 2
	OCR0A=0;
	OCR0B=0;
        //waits while polling the OCF0B bit signaling a compare match with OCR0B
	do ; while ((TIFR & 1<<OCF0B) == 0);
	TIFR = 1<<OCF0B;

                //loops through each bit in the 'code' variable
                //and emits a long or short pulse depending on value
		for (uint16_t Bit=0x8000;Bit;Bit=Bit>>1){
                //if 1
			if (code & Bit) {
			OCR0A=255;
			OCR0B=80;
		}
                //if 0
		else {
			OCR0A=130;
			OCR0B=80;
		};
		do ; while ((TIFR & 1<<OCF0B) == 0);
		TIFR = 1<<OCF0B;
	}
	//emits a short pulse signaling the end of the transmission
	OCR0A=130;
	OCR0B=80;
	do ; while ((TIFR & 1<<OCF0B) == 0);
	TIFR = 1<<OCF0B;

}

/*
*emits a transmission (preamble pulse, 16 bit of data + end pulse)
*/
void transmit(uint8_t address,uint8_t code){
	preamble();
	setupPWM();//PWM settings in TCCR0A and TCCR0B
	sendCode((JVC_ADDRESS<<8)+JVC_MUTE);
	stopPWM();//disables PWM (TCCR0A=0)
}

 

There are two issues:

  1. This code will generate the preamble pulse the first time, followed by glitched data, and then only the glitchy data when called consecutively.
  2. It needs the busywork 1/2 instructions I added in sendCode(), otherwise the 2 first bits of the transmission are always cut.

 

EDIT: The actual data (orange and yellow parts) and the stop pulse work fine when the call to preamble() is commented out. The only issue is with the preamble part.

 

I'd appreciate any help to properly output the opening pulse, and maybe fix point 2.

Last Edited: Fri. Oct 6, 2017 - 05:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Without meaningful comments your code makes my eyes hurt but I'll make a guess that it might be something to do with when OCR gets updated. Look at the table which describes the various modes and the column which says when the update happens. Sometimes it is instant; something not.

'This forum helps those who help themselves.'

 

pragmatic  adjective dealing with things sensibly and realistically in a way that is based on practical rather than theoretical consideration.

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

Brian Fairchild wrote:

Without meaningful comments your code makes my eyes hurt but I'll make a guess that it might be something to do with when OCR gets updated. Look at the table which describes the various modes and the column which says when the update happens. Sometimes it is instant; something not.

 

The table 11-5 p.79 says OCRx is updated at BOTTOM (0x00). I'm not sure how to account for this in my current code, or if I even need to.

I edited in some extra comments.

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

mr-maurice wrote:
I'm not sure how to account for this ...

 

Well, it depends.  How fast is the datastream?  Will the AVR have enough time to respond to each?

 

Is this really "PWM"?  I haven't deciphered the code.  I guess one could break a pulse train into PWM segments.

 

An "interesting" situation.  How many bits of preamble?  Then 16 bits of data?  The one bit-width of stop pulse?  Could that be an 8-bit packet?

 

If all of the above are multiples of 8 bits, then USART-as-SPI-master comes to mind.  But that AVR family doesn't have USART.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:

mr-maurice wrote:
I'm not sure how to account for this ...

 

Well, it depends.  How fast is the datastream?  Will the AVR have enough time to respond to each?

 

Is this really "PWM"?  I haven't deciphered the code.  I guess one could break a pulse train into PWM segments.

 

An "interesting" situation.  How many bits of preamble?  Then 16 bits of data?  The one bit-width of stop pulse?  Could that be an 8-bit packet?

 

If all of the above are multiples of 8 bits, then USART-as-SPI-master comes to mind.  But that AVR family doesn't have USART.

 

Going through your questions:

The longest transmission possible is preamble + 0xFFFF+ stop pulse, that's 47.28ms.

(Preamble is 8.5ms high then 4.1ms low, a high bit is 0.660ms high then 1.47ms low, and a stop pulse of 0.660ms)

 

It is PWM, the sendCode() method iterates through all 16 bits of data and sets the OCR0A and OCR0B registers for the next pulse according to each bit's value.

 

The preamble is just a long pulse, as described previously (8.5ms high, then 4.1ms low), then 16 bits of data, and a stop pulse.

Last Edited: Fri. Oct 6, 2017 - 03:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are there transitions within each bit time?

Is a bit high (or low) for the entire 2.034ms bit time?

(47.28 - (8.5+4.1+0.66+1.47)) = 32.55ms data time

therefore

32.55/16 = 2.034ms bit time

 

Edit: try to correct formatting that was not originally accepted

David (aka frog_jr)

Last Edited: Fri. Oct 6, 2017 - 03:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mr-maurice wrote:
Preamble is 8.5ms high then 4.1ms low
So lets start with the preamble.

void preamble(){
	//enables fast pwm, non inverting, prescaler 64
	TCCR0A = (1<<WGM01)| (1<<WGM00)|(1<<COM0B1);
	TCCR0B = (1<<WGM02)|( 1<<CS01)|(1<<CS00);
	TCNT0=0;
	OCR0A=255;
	OCR0B=80;
	do ; while ((TIFR & 1<<OCF0B) == 0);
	TIFR = 1<<OCF0B;
	TCCR0A = 0;
}

After waiting for the compare match (falling edge of the output signal) you disconnect the pin from the timer (WHY?). Possibly you get a glitch there (depends on the actual port setup). Then the function exists and the following code again changes the timer setup and sets the counter to 0. So what part of the code is supposed to generate the low phase of the preamble?

mr-maurice wrote:
and then only the glitchy data when called consecutively
Not even getting the high phase on consecutive calls is the result of not clearing the compare match flag at the beginning of the preamble.

Stefan Ernst

Last Edited: Fri. Oct 6, 2017 - 04:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:

mr-maurice wrote:
Preamble is 8.5ms high then 4.1ms low
So lets start with the preamble.

void preamble(){
	//enables fast pwm, non inverting, prescaler 64
	TCCR0A = (1<<WGM01)| (1<<WGM00)|(1<<COM0B1);
	TCCR0B = (1<<WGM02)|( 1<<CS01)|(1<<CS00);
	TCNT0=0;
	OCR0A=255;
	OCR0B=80;
	do ; while ((TIFR & 1<<OCF0B) == 0);
	TIFR = 1<<OCF0B;
	TCCR0A = 0;
}

After waiting for the compare match (falling edge of the output signal) you disconnect the pin from the timer (WHY?). Possibly you get a glitch there (depends on the actual port setup). Then the function exists and the following code again changes the timer setup and sets the counter to 0. So what part of the code is supposed to generate the low phase of the preamble?

mr-maurice wrote:
and then only the glitchy data when called consecutively
Not even getting the high phase on consecutive calls is the result of not clearing the compare match flag at the beginning of the preamble.

 

I edited my first post which was pretty unclear: the actual data part (orange and yellow parts on the picture) as well as the stop pulse work fine when the call to preamble() is commented out.

 

Since the data part works fine, I tried to reuse the same logic for the preamble but fell flat.

 

frog_jr wrote:

Are there transitions within each bit time?

 

Yes, each bit is an high,low sequence with set durations depending on the bit value.

 

Last Edited: Fri. Oct 6, 2017 - 05:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Trying to simulate IR tx remote?
Maybe CTC with counting variable will do the pulse width?
.
MG

I don't know why I'm still doing this hobby

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

MicroGyro wrote:
Trying to simulate IR tx remote? Maybe CTC with counting variable will do the pulse width? . MG

 

 

Yep.

 

Are you suggesting the kind of solution abcminiuser describes in part 3 here http://www.avrfreaks.net/forum/tut-c-newbies-guide-avr-timers?name=PNphpBB2&file=viewtopic&t=50106 ?

 

 

 

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

Part 5, yes.
ie, you can set the interrupt at every 660us so you can get ms value by multiply it.(you know what I mean) and use it to turn other timer(as 40khz carrier) on/off respectively.
Now you have IR transmitter with variable code to send.
.
Forgive me. Typing in tablet is challenging for me :)
.
MG

I don't know why I'm still doing this hobby

Last Edited: Sat. Oct 7, 2017 - 12:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks everyone. This is fixed for now, by disabling interrupts in the transmit function, and reversing to _delay statements for the preamble part.

void preamble(){
	PORTB |=_BV(PB1);
	_delay_us(8600);
	PORTB &=~_BV(PB1);
	_delay_us(3100);

}

void transmit(uint8_t address,uint8_t code){
	cli();//disabling interrupts
	preamble();
	setupPCM();
	sendCode((JVC_ADDRESS<<8)+code);
	stopPCM();
	sei();//re-enabling interrupts
}

 

sternst wrote:

mr-maurice wrote:
and then only the glitchy data when called consecutively
Not even getting the high phase on consecutive calls is the result of not clearing the compare match flag at the beginning of the preamble.

 

Thanks, will keep that in mind. I'll probably want to use timers for the preamble at some point.