Pulse width modulation (PWM) on Attiny 9 - avr

I am using an Attiny 9.
I connected a LED to PB1.
I wrote following code and upload it to the Attiny 9.
#define F_CPU 800000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/sleep.h>
#include <avr/power.h>
#define D0 PB1
int main(void)
{
power_adc_disable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
TCCR0A |= (1<<WGM00)|(1<<WGM01)|(1<<COM0B1);
TCCR0B |= (1<<WGM03)|(1<<WGM02)|(1<<CS00);
DDRB |= 1<<D0;
PORTB |= 1<<D0;
_delay_ms(3000);
PORTB &= ~(1<<D0);
for (uint8_t i = 255; 247 < i; --i)
{
OCR0B = i;
_delay_ms(70);
}
for (uint8_t i= 247; -1 < i; --i)
{
OCR0B = i;
_delay_ms(4);
}
while(1){
sleep_enable();
sleep_cpu();
}
return 0;
}
I want the LED to be lighten and darken. But after I uploaded the code, the LED illuminates continuously. I also read a datasheet of Attiny 9. I followed how to use PWM. But it's not working.
What's wrong?

There are a number of issues with that code.
You're putting the processor in to power down mode, which according to the datasheet, disables the clocks and only asynchronous events can take place.
You're setting it to use fast PWM mode with OCR0A as your top, but you aren't setting OCR0A anywhere. You won't generate any compare matches if you your timer can't count high enough. It would appear from your loops that you are expecting the timer to count to 255.
You only change the duty cycle of the PWM before your while loop, so even if your PWM gets set up correctly, you won't ever be able to see your LED pulse like you want.
Here's a link giving an example of how to set up a PWM and change its duty cycles:
http://allaboutavr.com/index.php/2017/05/13/chapter-5-timers-and-pwm-pulse-width-modulation/
Ty something like this:
#include <avr/io.h>
#include <util/delay.h>
#define D0 PB1
int main(void)
{
DDRB |= 1<<D0;
TCCR0A |= (1<<WGM00)|(1<<COM0B1);
TCCR0B |= (1<<WGM02)|(1<<CS00);
while (1) {
for (uint8_t i = 0; i < 255; i++) {
OCR0B = i;
_delay_ms(4);
}
for (uint8_t i = 255; i >= 0; i--) {
OCR0B = i;
_delay_ms(4);
}
}
}

Related

Atmega168 USART transmitter with interrupts

I try to transmit data from my Atmega168A-PU to my computer using the USART.
To do so I have written the following code:
#include <avr/io.h>
#include <stdlib.h>
#define F_CPU 8000000UL
#define USART_BAUD 9600
#define UBRR_VALUE (F_CPU/16/USART_BAUD - 1)
void
usart_init(void)
{
UBRR0H = (unsigned char)(UBRR_VALUE >> 8);
UBRR0L = (unsigned char)UBRR_VALUE;
UCSR0B = _BV(TXEN0) | _BV(UDRIE0);
UCSR0C = _BV(USBS0) | _BV(UCSZ00) | _BV(UCSZ01);
//UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
}
ISR(USART0_UDRE_vect)
{
UDR0 = '0';
UCSR0A |= _BV(TXC0);
}
int main(void)
{
usart_init();
while(!(UCSR0A & _BV(UDRE0)));
UDR0 = '0';
while(1);
return 0;
}
I have attached the Arduino USB2SERIAL converter to read the values on my computer, however the converter says that he does not receive data and my computer does not receive data either.
Note: My lfuse is 0xe2 (CLKDIV8 disabled), so I have 8MHz F_CPU.
Note: I tried without the UCSR0A |= _BV(TXC0);, too.
Note: I have a capacitor between AVcc and AGnd.
Note: Fuses: (E:F9, H:DF, L:E2)
Well the problem was pretty easy. I forgot the sei(); as #KIIV pointed out (also u/odokemono pointed that out). Thanks.
Also it is better to use the USART_TX_vect instead of USART0_UDRE_vect because my receiver is disabled. Here is the corrected code:
#include <avr/io.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#define F_CPU 8000000UL
#define USART_BAUD 9600
#define UBRR_VALUE (F_CPU/16/USART_BAUD - 1)
void
usart_init(void)
{
UBRR0H = (unsigned char)(UBRR_VALUE >> 8);
UBRR0L = (unsigned char)UBRR_VALUE;
UCSR0B = _BV(TXEN0) | _BV(TXCIE0);
UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
//UCSR0C = _BV(UCSZ00) | _BV(UCSZ01);
sei();
}
ISR(USART_TX_vect)
{
UDR0 = '0';
UCSR0A |= _BV(TXC0);
}
int main(void)
{
usart_init();
while(!(UCSR0A & _BV(UDRE0)));
UDR0 = '0';
while(1);
return 0;
}
By the way: I disabled the second stop bit as u/odokemono pointed out because my USB2SERIAL seems to work better without the second stop bit.

avr pd3 and pb3 work together as pwm

I'm trying the learn timers and pwm methods. I use the timer2 for fast pwm but i just want to use only pd3 for pwm. The code below works two pins together as pwm. How can i split these pins.
mcu is Atmega328p
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
volatile unsigned char duty_cyc_a,duty_cyc_b;
int main(void)
{
DDRB = 0b00001000;
DDRD = 0b00001000;
PORTD = 0x00;
PORTB = 0x00;
TCCR2A = 0b10100011;
TCCR2B = 0b00000001;
TCNT2 = 0;
OCR2A = 117;
OCR2B = 117;
duty_cyc_a=0;
duty_cyc_b=255;
while (1)
{
PORTB = (1<<3);
_delay_ms(1000);
PORTB &= 0;
_delay_ms(1000);
}
}
You should look at the manual, specifically the pages at the end of the section on timer2 to see how setting the registers controls the waveform and pins. The pins are called OC2A and OC2B, which are also names for PB3 and PD3.
Another tip for readable code is to use the macros that describe the pin functions, rather than combine them into one difficult binary number. If the previous writer had done that, your job would be easier.
TCCR2A = 0b10100011;
TCCR2B = 0b00000001;
should be
TCCR2A = 1 << COM2A1 | 1 << COM2B1 | 1 << WGM21 | 1 << WGM20;
TCCR2B = 1 << CS20;
Now you can see which bits the previous author set to 1. (The rest are set to 0).
Go through the tables in the register section to decide which pins you need to set in these two control registers instead. (You won't use COM2A1, and you will need to choose different WGMx pins.)

Atmel Studio- ATmega128 bootloader

I am trying to write a customized boot-loader for ATmega AVR's. I write a code, and it work perfectly in small AVR's like ATmega32A and ATmega8A. But when i want to use it in ATmega128A, it writes nothing in flash segment.
I'm sure Fuses are correct, ATmega103 mode is disabled, and the program starts at boot section, but it does nothing..
Before calling function "boot_program_page" I set PORTC and turn some LED's on, and after that I cleared PORTC and LED's goes off. so the code in executing completely too.
The function I am using is an example provided in avr/boot.h.
It should write some data ( 0x00 actually ) in page 0 of flash memory..
here is my code :
#define F_CPU 8000000UL
#include <inttypes.h>
#include <avr/io.h>
#include <avr/boot.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
void boot_program_page (uint32_t page, uint8_t *buf);
int main(void)
{
uint8_t data[SPM_PAGESIZE] = {0};
uint32_t page = 0;
DDRC = 255;
PORTC = 255;
page *= (uint32_t)SPM_PAGESIZE;
boot_program_page(page,data);
_delay_ms(1000);
PORTC = 0;
while (1)
{
}
}
void boot_program_page (uint32_t page, uint8_t *buf)
{
uint16_t i;
uint8_t sreg;
// Disable interrupts.
sreg = SREG;
cli();
eeprom_busy_wait ();
boot_page_erase (page);
boot_spm_busy_wait (); // Wait until the memory is erased.
for (i=0; i<SPM_PAGESIZE; i+=2)
{
// Set up little-endian word.
uint16_t w = *buf++;
w += (*buf++) << 8;
boot_page_fill (page + i, w);
}
boot_page_write (page); // Store buffer in flash page.
boot_spm_busy_wait(); // Wait until the memory is written.
// Reenable RWW-section again. We need this if we want to jump back
// to the application after bootloading.
boot_rww_enable ();
// Re-enable interrupts (if they were ever enabled).
SREG = sreg;
}

ATmega328P, simple counter interrupt example not working - frustrated

I honestly cannot figure out what I'm doing wrong here. I have a LED and an oscilloscope probe attached to the LED at port B3, but the output is flat 2V for some reason.
#include <avr/io.h>
#include <avr/interrupt.h>
#define LED 3
void tog(void)
{
PORTB ^= (1 << LED);
}
int main(void)
{
int i = 0;
DDRB |= (1 << LED);
TCCR0A |= (1 << WGM01);
OCR0A = 0xF9;
TIMSK0 |= (1 << OCIE0A);
TCNT0 = 0;
sei();
TCCR0B |= (1 << CS02);
while (1)
{
i++;
}
}
ISR (TIMER0_COMPA_vect)
{
tog();
}

pwm value not changing

I have written a pwm code for Atmega128. I am using fast pwm mode with non-inverting pulse on compare match and I need to change the OCR0 value at certain times. Yet it doesn't change. Anyone knows what is the problem here ??
#include <avr/interrupt.h>
#include <avr/io.h>
uint8_t tick_1sec;
void timer1_init(void) // 1 second timer
{
OCR1A = 15624;
TIMSK |= (1<<OCIE1A);
TCCR1B = (1<<WGM12); //CTC mode
TCCR1B |= (1<<CS12)|(0<<CS11)|(1<<CS10);
}
ISR(TIMER1_COMPA_vect) //1 second interrupt
{
cli();
tick_1sec = 1;
sei();
}
void timer0_init(void) // fast pwm with OC0 non-inverting mode
{
TCCR0 = (1<<FOC0)|(1<<WGM01)|(1<<WGM00);
TCCR0 |= (1<<COM01)|(0<<COM00);
TCCR0 |= (1<<CS02)|(1<<CS01)|(1<<CS00);
OCR0 = 63;
TIMSK |= (1<<OCIE0);
}
int main(void)
{
uint8_t t = 0;
DDRB = 0xFF;
timer0_init();
timer1_init();
sei();
while(1){
if (tick_1sec)
{
tick_1sec = 0;
t++;
if (t == 10){
OCR0 = 127;
}
else if (t == 20){
OCR0 = 191;
}
else if (t == 30){
OCR0 = 63;
t = 0;
}
}
}
return 0;
}
Things to check:
I recommend declaring tick_1sec as volatile to prevent the compiler of hyper-optimizing that register.
What is your clock frequency? Your ISR will deliver 1s calls only if your CPU frequency is 16MHz (==> 16.000.000 / 1024 / 15624)
You might have a LED in your hardware which you can invert from a) the ISR b) within the first if () in main to see if this is ever reached.
update: "volatile"
The link provided by #skyrift in his comment is very worth reading.
When you use Atmel Studio, compile your code once with/without the volatile keyword and compare what the compiler is doing ==> Solution explorer / Output Files / *.lss ... you will see each C statement and how the compiler converts it to machine code ... an exercise worth once in a while when working with micros ...

Resources