Friday, January 7, 2011

PWM Controller Design with AVR Microcontroller


Objective: Design a Pulse Width Modulation (PWM) controller with 3.0 MHz base frequency

Requirements:
  • Pulse width needs to be adjustable for a 3.0 MHz signal (the base frequency)
  • Pulses must be less than 100.0 nanoseconds wide
  • When the PWM output is activated, the number of pulses for a specified pulse width needs to be adjustable
Hardware:
Circuit Schematic:


S1 input switch:

MSB
7

6

5

4

3

2

1
LSB
0
E
C2
C1
C0
W3
W2
W1
W0


E = Enable input (enables the rest of the input pins)
C2 - C0 = Pulse Count Control Bits
W3 - W0 = Pulse Width Control Bits


Pulse Width Control Bits of S1 Switch:
Switch ON = 1
Switch OFF = 0

Base Frequency at PWM_OUT = 3MHz
W3
W2
W1
W0
Pulse Width
in nanoseconds
0
0
0
0
0
0
0
0
1
30
0
0
1
0
46
0
0
1
1
62
0
1
0
0
78
0
1
0
1
94
0
1
1
0
110
0
1
1
1
124
1
0
0
0
140
1
0
0
1
156
1
0
1
0
172
1
0
1
1
188
1
1
0
0
204
1
1
0
1
220
1
1
1
0
236
1
1
1
1
252

The rows highlighted in green contain acceptable pulse widths.


Pulse Count Control Bits of S1 Switch (can be reprogrammed):
Switch ON = 1
Switch OFF = 0

Base Frequency at PWM_OUT = 3MHz
C2

C1
C0
Pulse Count
N
Time for N Pulses at 3MHz
in microseconds
0
0
0
282
94.0
0
0
1
236
78.7
0
1
0
190
63.3
0
1
1
142
47.3
1
0
0
96
32.0
1
0
1
48
16.0
1
1
0
24
8.0
1
1
1
13
4.3


Output signals:
PWM_OUT = PWM signal with a base frequency of 3MHz
BUSY_OUT = BUSY_OUT stays HIGH until N number of pulses (defined by C2-C0 of S1 switch) have been generated at the PWM_OUT pin


ATTINY261 Assembly Code (Compiled with AVR Studio 4)

;++++++++++++++++++++++++
;I/O:
; PB3-PB0 = Output
; PB7-PB4 = Input
; PA7-PA0 = Input
;Special I/O:
; PB3 = Busy Output
; PB1 = PWM Output
; PA7 = PWM enable input
; ----------- Registers ----------
; R20 = Flags that a counting sequence has already began by 1 and 0 Otherwise
; R21 = Clock for PWM (Timer1)
; R22 = Clock for Timer0
; R23 = Always Set to 0 to reset the clocks on interrupts
; R24 = Always holds 0x01 for PORTB (Reset value for PORTB)
; R5 = PinID on PortB for Busy Signal
; R10 = Loaded with 0x0F for duty cycle selection
; R11 = Loaded with 0x07 for pulse count selection
;++++++++++++++++++++++++

.include "tn261def.inc"

.DSEG
Pulse_Count:  .BYTE  16
.CSEG
.org 0x0000
RJMP RESET;

.org OC0Aaddr
RJMP Output_Match0_ISR;

RESET:
; Load the stack pointer
LDI R16, LOW(RAMEND);
LDI R17, HIGH(RAMEND);
OUT SPL, R16;

;-------- Register Initialization -----------
LDI R20, 0x00;
LDI R21, (1<<CS10);
LDI R22, (1<<CS00);
CLR R23;
CLR R24;

LDI R16, 0x08;
MOV R5, R16;

LDI R16, 0x0F;
MOV R10, R16;

LDI R16, 0x07;
MOV R11, R16;
;------------------- Input/Output Setup (Start) ----------------------
; Set PB3-PB0 = Output
LDI R16, (1<<PB3)|(1<<PB2)|(1<<PB1)|(1<<PB0);
OUT DDRB, R16;
LDI R16, 0x02;
OUT PORTB, R16;
; Set PA7-PA0 = Input
LDI R16, 0x00;

OUT DDRA, R16;
; Set Initial output to PWM (PB1) line to 5 volts --> in R23
LDI R24, 0x01;
;------------------- Input/Output Setup (End) ----------------------


;------------------ Pulse_Count Initialization (START) ----------------
; Load the base address of Pulse_Count into Z registers (R31:R30) = (H:L)
LDI R30, low(Pulse_Count);
LDI R31, high(Pulse_Count);

;Load 0x0753 (Low Byte First)
LDI R16, 0x53;
ST Z+, R16;
LDI R17, 0x07;
ST Z+, R17;
;Load 0x061B (Low Byte First)
LDI R16, 0x1B;
ST Z+, R16;
LDI R17, 0x06;
ST Z+, R17;
;Load 0x04E2 (Low Byte First)
LDI R16, 0xE2;
ST Z+, R16;
LDI R17, 0x04;
ST Z+, R17;
;Load 0x03AA (Low Byte First)
LDI R16, 0xAA;
ST Z+, R16;
LDI R17, 0x03;
ST Z+, R17;
;Load 0x0271 (Low Byte First)
LDI R16, 0x71;
ST Z+, R16;
LDI R17, 0x02;
ST Z+, R17;
;Load 0x0139 (Low Byte First)
LDI R16, 0x39;
ST Z+, R16;
LDI R17, 0x01;
ST Z+, R17;
;Load 0x009C (Low Byte First)
LDI R16, 0x9C;
ST Z+, R16;
LDI R17, 0x00;
ST Z+, R17;
;Load 0x004E (Low Byte First)
LDI R16, 0x4E;
ST Z+, R16;
LDI R17, 0x00;
ST Z, R17;

;Reset Z to base address of Pulse_Count
LDI R30, low(Pulse_Count);
LDI R31, high(Pulse_Count);
;------------------ Pulse_Count Initialization (END) ------------------


;------------------- Setup Timer 0 (START) -------------------------
; Set the width of the counter to 16
LDI R16, (1<<TCW0);
OUT TCCR0A, R16;
; Set the output compare value to 1875d = 0x0753--- (1875 x 3.2 = 6000)
LDI R17, 0x07;
LDI R16, 0x53;
OUT OCR0B, R17; Write the High Byte
OUT OCR0A, R16; Write the Low Byte
; Enable Output compare match A
LDI R16, (1<<OCIE0A);
OUT TIMSK, R16;
;------------------- Setup Timer 0 (END) -------------------------


;---------------------- Setup Timer 1 (START) ----------------------
; Enable PLL for the peripheral clock..
LDI R16, (1<<PLLE);
OUT PLLCSR, R16;

;Wait of PLL to LOCK
WaitLock:
       IN R16, PLLCSR;
       SBRS R16, PLOCK;
RJMP WaitLock;

; Set PLL as the PWM or the peripheral clock source (PCLK)
IN R16, PLLCSR;
LDI R17, (1<<PCKE);
OR R16, R17;
OUT PLLCSR, R16;

; Enable PWM1A and toggle OC1A on match
;LDI R16, (1<<COM1A0)|(1<<PWM1A);
LDI R16, (1<<COM1A0)|(1<<PWM1A);
OUT TCCR1A, R16;

; Set Output Compare match value..--> sets the base frequency for 3MHz
LDI R16, 0x14;
OUT OCR1C, R16;

; Set the duty cycle.. 0x7F = 127 for 50%
LDI R16, 0x04;
OUT OCR1A, R16;

; Set the prescalar time.. then go..
;LDI R16, (1<<CS10);
;OUT TCCR1B, R16;
;---------------------- Setup Timer 1 (END) ----------------------

;Enable Interrupts
SEI;

; Turn off Busy Signal..
OUT PORTB, R24;

; In the Loop, Do the following..
; 1. Check R20 to see if a counting sequence is in progress; if so loop again, otherwise goto 2
; 2. Read PortA and if a correct input is found, start the sequence, Set Busy signal to high
LOOP1:
       ; Check R20
       SBRC R20, 0;
       RJMP LOOP1;
      
       ; Turn off Busy Signal..
       OUT PORTB, R24;
IN R18, PINA;
       SBRS R18, 7; For STK500
       ;SBRC R18, 7; For prototype PCB
       RJMP START_PWM;
RJMP LOOP1;


START_PWM:

       ; Move R19 <- R18
       MOV R19, R18;
       ; Complement R19
       COM R19;
      
       ; ---- Set the duty cycle from R19 (START) ------
       ; Move R19 into R8
       MOV R8, R19;
       ; Select the last 4 bits (R10 = 0x0F)
       AND R8, R10;
       ; Load duty cycle value into OCR1A
       OUT OCR1A, R8;
       ; ---- Set the duty cycle from R19 (END) --------
      
       ;----- Set the Pulse_Count from R19 into OCR0B/OCR0A (START) ----
       ; Set Z register to the base address
       LDI R30, low(Pulse_Count);
       LDI R31, high(Pulse_Count);
       ; Move R19 into R9
       MOV R9, R19;
       ;Swap R9
       SWAP R9;
       ; Keep last 3 bits in R9
       AND R9, R11;
       ; Multiply Location counter by 2
       LSL R9;
       ; Clear Carry
       CLC;
       ; Calculate address into Z register to get the Pulse_Counter from R9;
       ADD R30, R9;
       ADC R31, R23;
       ; Load pulse counter from memory into R16 (low byte) and R17 (high byte)
       LD R16, Z+;
       LD R17, Z;
       ; Set 16-bit output compare match value of timer-0
       OUT OCR0B, R17; High byte
       OUT OCR0A, R16; Low byte
       ;----- Set the Pulse_Count from R19 into OCR0B/OCR0A (END) ------

       ; Set Busy Signal
       OUT PORTB, R5;
       ; Set R20 to (1 = R5)
       LDI R20, 0x01;
       ; Start Timer-0
       OUT TCCR0B, R22;
       ; Start Timer-1;
       OUT TCCR1B, R21;
RJMP LOOP1;

Output_Match0_ISR:

       ;; Stop Timer-0;
       OUT TCCR0B, R23;
;; Stop Timer-1;
       OUT TCCR1B, R23;
       ; Turn off Busy Signal..
       OUT PORTB, R24;
       ; Set R20 to 0
       CLR R20;

       ; Clear Timer 0 register..
       CLR R17;
       CLR R16;
       OUT TCNT0H, R17;
       OUT TCNT0L, R16;
       ; Clear Timer 1 register..
       OUT TCNT1, R16;
RETI;


--------------------------------------  Results Section -----------------------------------------------------------
> To be updated

No comments:

Post a Comment