I'm writing Arduino UNO (=ATMega328P-PU) programs in assembly to save memory, so I use avra.exe (same as atmel studio's avrasm32) to compile and avrdude to upload, and simple programs like blinking run fine. But now i tried to half-bright a LED with pwm. I checked up my code for errors but I didn't find any, but the LED just full brighs. I checked wiring too. Here's my pwm.asm code:
.nolist
.include "m328pdef.inc"
.list
.cseg
.org 0x00
rjmp start
.org 0x34
start: sbi ddrb, 5 ;pin 13
sbi portb, 5 ;pin 13 on, just to compare with the PWMed led
sbi ddrd, 5 ;pin 5 pwm
ldi r16, 0b00100011 ;fast pwm mode, non inverted pwm at oc0b = pin 5
out tccr0a, r16 ;I'm using Timer0
ldi r16, 0b00000001 ;no prescaler
out tccr0b, r16
ldi r16, 128 ; duty cycle = 50%
out ocr0b, r16
loop: rjmp loop
I tried to change duty cycle value but nothing changes.
I found myself the solution. The code is correct and the wiring too. Just setting the duty cycle to 50% doesn't make a difference for the human eye. So I tried to put 16 in ocr0b (duty cycle = 6.25%) and then i noticed a real difference. This is because it follows a logarithmic logic, so the difference between a duty cycle of 50% and 100% is actually very small
Related
I am trying to configure a timer (TIM9) and its interrupt handler to single step through a program. My approach is to first interrupt the first instruction, and then in the interrupt handler configure the timer so that it triggers an interrupt right after returning from the ISR.
Right now I'm still trying to get the first step correctly.
This is the piece of sample code I'm working with right now. I have configured the timer to generate an interrupt when its counter is equal to some compare value.
I set the compare value equal to 1 so that the code gets interrupted after 1 cycle of the timer. According to the reference manual the counter starts counting 1 cycle after setting the enable bit so I added a NOP.
/*
* TIM9 is configured to generate an interrupt when counter s equal to compare value
*/
TIM9->CCR1 = 1; // set compare 1 value to 1
TIM9->CR1 |= 1; // enable TIM9
__ISB(); // flush pipeline
__asm volatile ("NOP"); // from reference manual: counter starts counting 1 cycle after setting CEN bit
__asm volatile("MOV r8, #1 \n\t"); // expect to interrupt this instruction
__asm volatile("MOV r8, #2 \n\t");
__asm volatile("MOV r8, #3 \n\t");
__asm volatile("MOV r8, #4 \n\t");
To verify that the correct instruction got interrupted I used GDB to check the content of register r8 after entering the interrupt handler, and I found that it is actually equal to 6. This implies that there is a much longer delay than 1 cycle or that I'm simply missing something.
I can simply add 5 NOP instructions before the first MOV instruction so that the interrupt occurs at the right time but I don't understand why this is neccessary. As far as I know the code as I have it right now should generate an interrupt during the first MOV instruction.
Why am I getting this behavior? Why does there seem to be such a long delay between enabling the timer and the interrupt being generated?
Is this maybe because there is a delay between the counter value equaling the compare value and the actual generation of the interrupt?
Or is it possible that this has something to do with how the pipeline works?
I have set the prescaler of the timer to 0 and there is no internal clock division happening so the timer should have the same frequency as the system clock.
I do not know why you want to check it. It works as it is supposed to work.
NOP is the worst way of adding the delay. It is something different than the 8 bits AVR`s NOP. It is instantly flushed off the pipeline and it should be used only as padding.
1 clock after CEN means one timer clock, not the HCLK clock (they can be the same)
CCRx = 1 means two clocks.
If you run from FLASH - wait states are added
Pipeline execution status is not easy to determine. The actual instruction interruped does not have to be the one you actually think. The time of the interrupt is determined but not the actual instruction.
here is a simple 7-segment display with a pushbutton the problem is whenever I make the clock 1 MHZ the display doesn't run as expected but when I use 8 MHZ clock it works fine.
here is the code:
#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRD &= ~(1<<PD4);
DDRC |= (1<<PC0) | (1<<PC1) | (1<<PC2) | (1<<PC3);
PORTC = 0;
while (1)
{
if(PIND & (1<<PD4)){
_delay_ms(25);
if(PIND & (1<<PD4)){
PORTC++;
}
}
}
}
F_CPU Should be the same as the hardware fuses configuration in proteus you could change them by double-click on atmega 16 and change CKSEL Fuses as following
some info maybe help
_delay_ms() It only west some CPU cycles depend on the required time by using F_CPU in the calculation
You need to increase this delay to 300ms to make sure that program does not process the same click more than one and if you hold the key down it will increase in visual manner
analysis of the wrong behavior
25ms is a very short time ... a normal human click will take around 200-300ms so in every click the microcontroller will consider it more than one
when I use 8 MHZ clock it works fine
when you change F_CPU to 8MHZ the _delay_ms() it make the calculation on this speed and will west more cycles ... while the actual speed is 1MHZ
this differance in speed (between F_CPU and the actual speed) led to make 8 times slower delay ='25ms' *8 ='200 ms'
simple solution increase the _delay_ms(25) to _delay_ms(200) to make the same effect
update (info about how delay work)
_delay_ms Is just LOOP to waste CPU cycles and it blocks the CPU from work
the frequency of the microcontroller is determined by the hardware fuses so you need to tell the software which frequency you use throw define F_CPU so the software will know each cycle will take time = 1/F_cpu
when you need a delay, the software already know the amount of time taken by each clock so it will calculate the number of cycle to achieve the required delay time(if you need delay 1ms delay and each clock tack 1 us then you need to wait for 1000 cycles to achieve these delay)
in Assembly there is instruction called nop take only 1 cycle to execute and do nothing
the following code is not correct but it makes something similar when the compiler translate _delay_ms() in Assembly
for(int i=0;i<50;i++)nop;
this code will make 50 nop and wast 50 cycles(actually more than 50 because access and increment variable 'i' will consume some cycles but neglect it to show the idea)
read more
Do AVR delay functions use timers?
how to make delay in AVR Assembly
I am using a 328P (Running on the internal 8mhz) to generate a Square wave at around 111K hz or 120K hz with and adjustable duty cycle.
I'm fairly new to doing this level of hardware manipulation so please excuse my ignorance, I spent 3 days looking online and in the datasheet but I'm afraid my understanding of what the acronyms and abbreviations mean is still too limited.
So far I have come up with the following code after doing the research online and in the datasheet. But I feel like I'm missing something, What register manipulates frequency and what register manipulates duty cycle in (Datasheet 20.12.5) Phase and Frequency Correct PWM Mode?
void setup(){
DDRB |= (1 << DDB1); //Set PB1 as output
OCR1A = (Unsure of what TOP should be here);
TCCR1A = (1 << WGM10) | (1 << COM1B0) | (1 << COM1A0);
TCCR1B = (1 << CS10) | (1 << WGM13);
}
void loop(){
//manipulate duty cycle with other code here
}
What am I missing or what should I be doing differently here?
I tried to use the online AVR Timer Calc to help me get clock ticks needed for that frequency. It says a total of 72 Timer ticks with no prescaler and 16 bit timer would yield a 111Khz (approximate) Square wave. Jumping to 73 or 71 jumps the frequency too much out of the desired range. is there any way to get that closer on an AVR?
After a lot of research along side a friend of mine who is also an EE and works with embedded systems we came to find the best solution for getting what I needed out of the 328p. I will post the solution below for anyone who is interested and needs something similar.
void setup() {
// put your setup code here, to run once:
//Set Timer1 for around 109Khz signal
cli();//stop interrupts
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
//Set register bits
TCCR1A = _BV(COM1A1) | _BV(WGM11);
TCCR1B = _BV(CS10) | _BV(WGM12) | _BV(WGM13);
ICR1 = 73; // frequency = 8000000/73 = 109.589 kHz
OCR1A = 0; //0% Duty Cycle or Full Off.
sei();//allow interrupts
}
void loop() {
// put your main code here, to run repeatedly:
OCR1A = 36; //50% Duty Cycle 73/2 = 36.5 Can be changed as needed.
}
This code sets the registers for Timer 1 so it shouldn't interfere with millis or other timing functions in the Arduino libraries. I needed to manipulate a specific pin anyways and PB1 (OC1A) (or Arduino digital pin 9) is the pin that this will oscillate.
You can change ICR1 to any value you need based on some simple math,
Your clock frequency divided by the value of the counter equals the approximate frequency produced. OCR1A sets the duty cycle of the signal.
You are limited in the exact frequency but for my needs this worked out OK.
I was still able to use it to drive the transducer I was using.
This was a quick answer to the initial problem and allows me to change the duty cycle as a bonus. I don't remember the exact information on the registers we set, When I have time I will update this answer with the info from the data sheet pertaining to that.
In the title, you asked "ATMega328P How to set TIMER1 PWM Frequency". You tune the frequency by setting the TOP value to what you want it to be. A smaller TOP value means the counter reaches TOP sooner, so the frequency is higher. The prescaler and clock source you use for the timer determines how fast it counts up, so that also affects the frequency, what ranges of frequencies are possible, and the resolution you get for setting the frequency. Your TOP value also determines how much resolution you have when setting the duty cycle, since the duty cycle should be an integer less than or equal to TOP.
Near the bottom of your post, you asked "is there any way to get that closer on an AVR?" Since your AVR is running at 8 MHz, every frequency you can generate with its timers will be of the form (8 MHz)/N, where N is some integer. That's because the timers can't do anything or change anything between ticks of the clock source you are supplying to them. Timer 1 will get a clock tick 8 times per microsecond, and each time that clock tick happens it can advance its count and/or control a PWM pin and/or fire an interrupt. I think you already figured this out using the online AVR timer calculator. Here are some of the achievable PWM frequencies you can get on an AVR running at 8 MHz:
8000 kHz / 73 = 109.6 kHz
8000 kHz / 72 = 111.1 kHz
8000 kHz / 71 = 112.7 kHz
If you need more precise frequency tuning, you need to get a faster clock source in your system one way or another. You could get a faster microcontroller or maybe figure out how to supply a faster clock as an input to a timer on the microcontroller.
I'm using an ATMega128 micro and have all of my pin inits set to output and set to low under my main section of code:
PORTB=0x00;
DDRB=0xFF;
However on startup, the output associated with PORTB.0 flicks high for a split second (I've caught it on the scope) and it seems the other outputs are doing the same. Seems like it goes LOW-HIGH-LOW. I've done some reading that it could be caused by the tri-state to output switch during startup, so I've set the PUD register to 1 before the pin inits and then back to 0 after and still no luck. Does anyone have any other ideas to keep that output off during startup? It doesn't always occur either which is what has me stumped.
The fundamental problem is a hardware issue - lack of a pull-down resistor on the GPIO so that it is floating when in the reset-default high-impedance input state.
The best you can do in software is to initialise the GPIO at the earliest opportunity immediately after the reset. To do this in CodeVisionAVR you need to use a customised startup.asm in your project as described in section 4.18 of the CoadeVisionAVR compiler manual:
...
Where I suggest you initialise PORTB and DDRB as follows:
LDI R16, 0x00
OUT PORTB, R16
LDI R16, 0xFF
OUT DDRB, R16
immediately before step 2, i.e. the first four instructions. The amount of time the GPIO will be left floating will possibly be too small for the relay to react if it is a mechanical relay. You may still have a problem for a solid state relay. The length of any pulse may depend on the power-supply rise time; if it is slow, you may get a longer pulse.
I'm trying to hijack the Timer interrupt. A colleague told me that interrupt 0x08 on the IDT (Interrupt Descriptor Table) is the timer. Of curse I checked and saw two possible answers: this which says that 8 is the real clock timer and this saying it's the Double Fault interrupt - I decided to believe him and not waste time on checking further. After finally having control over the IDT and replacing interrupt 8, nothing is happening.
So what is going on?
Did this interrupt change its purpose over time from timer to double fault?
Does this interrupt has different purposes on ARM/Intel/etc.?
My code is a kernel module, that hijacks the interrupt 8 and simply do a printk command every time the interrupt arrives. I ran it for about 25 minutes - No output in dmesg.
In case it matters: I run Linux Mint with kernel 3.8 on a VM. The host has an Intel i5.
You can find which interrupt is for timer by using this command: cat /proc/interrupt
Following is a sample output on a 6 core machine:
cat /proc/interrupts | egrep "timer|rtc"
0: 320745126 0 0 0 0 0 IO-APIC-edge timer
8: 1 0 0 0 0 0 IO-APIC-edge rtc0
LOC: 115447297 304097630 194770704 212244137 63864376 69243268 Local timer interrupts
Note, timer and rtc are different. Also there is only one rtc interrupt so far. (Lots of timer interrupts). Following is the uptime output.
uptime
14:14:20 up 13 days, 3:58, 9 users, load average: 0.47, 1.68, 1.38
I think you should check this before you hack IDT. Also, probably, you want to hack interrupt 0, not 8.
You have found two descriptions for the same IRQ because in protected mode the address range 0x0 - 0x1F are reserved for internal cpu interruptions use.
You have to remap IRQs to another address space without conflicts, in this article you can find it explained with all the source code needed:
https://alfaexploit.com/readArticle/416