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:
- 20.0 MHz AVR ATTINY261 MCU (http://www.atmel.com/dyn/products/product_card.asp?PN=ATtiny261)
- NAND Logic Gates
- 5.0 V Power Supply
- 20.0 MHz Crystal Oscillator
- Resistors
- Capacitors
- Switches
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 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 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