NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Add on/off fucntion to pwm port

May 18, 2011
by kemil
kemil's Avatar

How do i add an on off function to the following code. ie when the port (PB1) is on the output is regulated by PWM but when its off the voltage is zero....like the nerdkits led_blink code(but this does work). Ive Tried everything!!

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

int main(void)

 {

 uint16_t brightness ;
 ICR1 = 1843;

 DDRB |= (1<<PB1)|(1<<PB2);         
 TCCR1A = (1<<COM1A1)|(1<<COM1B1) | (1<<WGM11);
 TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);

 uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar,     
  _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

while(1) {

if  (uart_char_is_waiting()) {
  scanf_P(PSTR("%d"), &brightness);
  }
  OCR1B = OCR1A= brightness;

 }
 return 0;
}
May 18, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi kemil,

Does the code above work? As far as setting the brightness goes I don't see anything obviously wrong with the above code (assuming there are real numbers coming in over the serial port). I would initialize your brightness variable to a real number just so that the first time around a real number is assigned to OCR1A and B.

How are you trying to turn off the pin? Your best bet is to both disconnect the output compare pin (set COM1A1 to 0) and make the pin into an input (high impedance) pin. Then when you are ready to turn it on again, just make the pin an output pin again, and reenable the output compare.

Humberto

May 19, 2011
by kemil
kemil's Avatar

Hi Humberto, Yea the code above works well... its just the implementation of the on/off part which doesnt work. I tried what you said by putting:

delay_ms(1000);

TCCR1A &= ~(1<<COM1A1);
DDRB &= ~(1<<PB1);

delay_ms(1000);
TCCR1A |= (1<<COM1A1);
DDRB |= (1<<PB1);

Underneith the, 'OCR1B = OCR1A= brightness;', line in my previous code.

It now goes on and off BUT when i try to set the PWM through my python gui something goes wrong... i cant seem to set OCR1A/B to the intended brightness and i have no idea why!

FYI, ive also put the pwm initialisation code into a function called pwm_init() like in the servosquirter code and put pwm_init() and the top of the while loop but that doesnt help either...

Cheers

Kemil

May 19, 2011
by kemil
kemil's Avatar

im pretty sure its something to do whith the line

if  (uart_char_is_waiting())

because when i get rid of it and just have the

scanf_P(PSTR("%d"), &brightness);

line by its self it loops round once but and i can adjust the brightness it just doesnt loop round again... think thats because the scanf function is waiting for something to read... if i get rid of the whole if statement including the scanf_P command and replace it with brightness = uart_read() again i dont get the desired result.

kemil

May 19, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Hi kemil,

The easiest solution to this problem is to remove the check to uart_char_is_waiting and just keep sending the current "brightness" setting from your GUI over the serial port. This would even let you adjust the brightness with a smooth slider like widget.

I'm not really sure why uart_is waiting is getting hung up. I suppose its possible your application is sending extra characters (like spaces or newlines), that are not getting consumed as part of the scan_f. This character is left waiting, which means the test passes and gets back around to the scan_f which then waits for a new number.

Humberto

May 20, 2011
by kemil
kemil's Avatar

If that was the case than it would'nt work when i take the following lines away...

delay_ms(1000);
TCCR1A &= ~(1<<COM1A1);
DDRB &= ~(1<<PB1);
delay_ms(1000);
TCCR1A |= (1<<COM1A1);
DDRB |= (1<<PB1);

But it does, it works perfectly. Both pins show the expected average voltage meaning the pwm is changing on demand. So why is it that when i add those 6 lines everything goes wrong?

May 20, 2011
by kemil
kemil's Avatar

im pretty sure the delay_ms(1000) is doing something to the data coming over the serial port. Because if i put OCR1B = brightness and OCR1A = 900 and put a dealy_ms(1000) above them PB1 still have the predicted voltage across it but PB2 (OCR1A)displays zero volts...

kemil

May 20, 2011
by kemil
kemil's Avatar

How do you add a delay whilst still being able to receive data....

May 20, 2011
by Noter
Noter's Avatar

To delay (or anything else) while still receiving data will require an interrupt service routine on the Rx pin. You can find a simple example on this thread http://www.nerdkits.com/forum/thread/1597/.

May 20, 2011
by kemil
kemil's Avatar

Hi Noter, Thanks for your help. Could you explain the lines whithin the interrupt... im not sure how exactly to implement them into my code. I guess i just want the interrupt to trigger when i change the value of my pwm register (done from a python gui). what is UDR0 doing in your code?

cheers kemil

May 20, 2011
by Noter
Noter's Avatar

UDR0 is the data register of the USART. All characters coming in or going out will go thru UDR0. I don't think it will be so easy to implement an interrupt routine and still use scanf. You would have to buffer the characters as they come in and do the scanf on the buffer. You might also have to consider what happens when the buffer is full but scanf has not yet read the buffer contents.

May 20, 2011
by Noter
Noter's Avatar

Maybe you could just add a command code to your current methodolgy and use it to determine action. For example, you could send "B 100" to set the brightness to 100, "E 0" to turn off the LED, and "E 1" to turn it back on. Then your scanf would use a format string something like "%1c %d" to read a character followed by a number.

May 20, 2011
by hevans
(NerdKits Staff)

hevans's Avatar

Noters solution above, embedding a command as well as a value in the string coming from the PC sounds like the best solution to your problem I can think of. The other possibility is to use timer interrupts on the chip (instead of delays) to turn the PWM pin on and off at your desired intervals.

Humberto

May 22, 2011
by kemil
kemil's Avatar

so i need an interrupt like this...

ISR(USART_RX_vect)
{
   char ReceivedByte;
   ReceivedByte = UDR;

}

Ive done some reading and following the suggestions made by Noter it seems i need to add a ring buffer to this. But i dont rly understand the coding of the ring buffers

I found this in another post:

ISR(USART_RX_vect)
{
unsigned char data;
unsigned char tmphead;
data = UDR0;

tmphead = ( USART_RxHead + 1 ) & USART_RX_BUFFER_MASK;
USART_RxHead = tmphead; 
if ( tmphead == USART_RxTail )
{
USART_RxBuf_Status = OVERFLOW;
return;
  }
USART_RxBuf[tmphead] = data;
USART_RxBuf_Status = NORMAL ;

But after the 'data = UDR0;' line i dont realy understad whats happening...

Additionally im confused about how i go from having the the buffered data to allocating it to my brightness variable which is subsequently assigned to OCR1B and OCR1A.

I think it would help if someone explains what exactly is happening once i send some data over the serial port.

So for example if i type in the number 1800 into my gui and it gets sent to the MCU what exactly happens next.... (side point: how does the number 1800 (11100001000) get broken down into its bytes)

(also what are the consequences of having a serial.write("n") command in my python gui after the serial.write("brightness") line ... is this interfering with anything because if i get rid of it i cant receive data at all... even without the whole on off problem)

Thanks everyone for their help so far, i really appreciate it.

Kemil

May 22, 2011
by bretm
bretm's Avatar

A ring buffer has a "head" position where incoming data is written and a "tail" position where outgoing data is read. The head chases the tail around the ring. First, the code above calculates what the new head value is going to be by adding 1 to the old head ( USART_RxHead + 1 ) and then wrapping around to zero when it reaches the end of the buffer (& USART_RX_BUFFER_MASK).

If the new head value would crash into the tail value it sets an overflow error status to indicate that there is no more room in the buffer. Otherwise it adds the data to the buffer. It's an error that it is updating USART_RxHead before doing this test. "USART_RxHead = tmphead;" should be moved to after the "if ( tmphead == USART_RxTail )" statement.

May 23, 2011
by kemil
kemil's Avatar

Thankyou for that bretm, i think understand the concept a bit better now, the only thing which im not sure is putting this into the context of what i want to do.... let me explain:

I want to be able to change the value of OCR1A/B which, at the moment can be any where between 1843 and 0. The decimal number 1843 is a 12 bit binary number and 0 is only one bit. So how does this ring buffer accomodate for such a range of values...

As a side note, in the above example would data in the line 'USART_RxBuf[tmphead] = data;' be the final fully buffered number which i could assign to OCR1A/B

Thanks for your help

kemil

May 23, 2011
by kemil
kemil's Avatar

Also what are the following declared as:

USART_RxHead

USART_RX_BUFFER_MASK

USART_RxTail

USART_RxBuf_Status

OVERFLOW

USART_RxBuf

NORMAL

Thanks

kemil

Post a Reply

Please log in to post a reply.

Did you know that 20 LEDs can be controlled from 11 microcontroller pins, to make a twinkling heart outline? Learn more...