PDA

View Full Version : Generate sawtooth with PIC?



gmglickman
- 2nd August 2010, 16:56
I'd like to generate a sawtooth wave, 0 to +1.5v at several hundred Hz up to 1 kHz on a 16F690 or similar, to generate a time base for CRT.

Suggestions for an efficient way to do this on a PIC?

My alternatives are a 555 and RC circuit or parallel DAC. I've tried an I2C DAC, but it's too slow with I2CWRITE commands.

Thanks.

ScaleRobotics
- 2nd August 2010, 17:45
One way to do it would be to use Darrel Taylor's instant interrupts and PWM with a lookup table.

You could use this code on a PIC18, and just change the lookup values to a linear increase, dropping back to zero when it gets to 1.5v. http://www.picbasic.co.uk/forum/content.php?r=229-Sine-wave-using-DT-interrupts . You would have to modify the speed of the interrupt. You could probably get by with a lot less steps.

For instance, the sinewave generated by the above code, is kind of like the red sine wave in this picture. If you were to change all the values in the lookup, you could generate your sawtooth, like the one in blue.

You can pretty much change the lookup table to create any wave form you like. Since your's is linear, you wouldn't really need a lookup table, you could just do some addition inside your interrupt handler, to increase the duty. And drop to zero after reaching X.

http://www.picbasic.co.uk/forum/attachment.php?attachmentid=4656&d=1280767125

Let us know how it goes, whichever way you choose to go.

Walter

aratti
- 2nd August 2010, 18:27
... Or you can use a CLD. (see this application note) http://www.centralsemi.com/PDFs/products/cld_application_notes.pdf

Al.

Acetronics2
- 3rd August 2010, 17:21
I'd like to generate a sawtooth wave, 0 to +1.5v at several hundred Hz up to 1 kHz on a 16F690 or similar, to generate a time base for CRT.

Suggestions for an efficient way to do this on a PIC?

My alternatives are a 555 and RC circuit or parallel DAC. I've tried an I2C DAC, but it's too slow with I2CWRITE commands.

Thanks.

Hi,

missing how many bits for resolution ... ;)

Alain

Charles Linquis
- 4th August 2010, 01:26
If you build a D->A using an R-2R ladder network, my guess is that you could get well over 3Khz with 8 bit resolution. Simply increment a value and write it to a port. No need to do more, since 255 + 1 = 0.

Charles Linquis
- 4th August 2010, 01:31
I keep forgetting that you may be running at 4Mhz. In that case your max freq may be lower. I run everything at 40Mhz, so my comments are calibrated to that value.

gmglickman
- 16th August 2010, 17:29
Great suggestions by some of the most knowledgable contributors to the list.

I elected to go with a simple software approach using PWM, without lookup tables. Most of the code is to provide DT's Instant Interrupt.

I initialize the 16F690 HPWM at 32kHz, zero duty. With each TMR1 interrupt I increment the duty register until I reach the desired maximum voltage on the sawtooth, then start over at zero. A low-pass RC filter smooths the output. Peak voltage and frequency are easily modified. I retained the oscope interrupt timing signal on RC.4; you will have to change some parameters if this is omitted.

Other ways to tackle this include a 555-based generator, R2R ladder network or dedicated parallel-interface DACs. Microchip has an application note http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1824&appnote=en011071 that may be helpful.

Gary



'************************************************* ***************
'* Name : oscope clock 16F690 sawtooth generator
'* Author : gmg Copyright (c) 2010 (yeah, right...)
'* Date : 7/28/2010
'* Notes : generates sawtooth wave for CRT time base
' Horiz: 1.5v=10 div, Vert: 2.0v=8 div max on CRT
passive low pass RC filter on PWM output RC.5
'************************************************* ***********
@ __config _INTRC_OSC_NOCLKOUT & _MCLRE_OFF & _CP_OFF & _WDT_OFF
ADCON1=$0F 'disable A/D
VRCON=0 'volt ref off
CM1CON0.0=0 : CM2CON0.0=0 'comparators off
ANSEL=0 : ANSELH=0
OSCCON=%01110000 ;8MHz intosc
DEFINE OSC 8
'-------------- pins
'PWM_out var portc.5
TRISC=0:TRISB=0
PORTC=0: PORTB=0
'-------------- variables
version con 107
i var byte
j var byte
temp var BYTE
tempword var word
duty_reg var word
HLevel var byte
VLevel var byte
HOrigin var byte
CLEAR
read 0, temp
if temp<>version then write 0, version 'store sw version in EEPROM 0

'---- setup HPWM on PORTC.5
hpwm 1, 0, 32000 '32kHz PWMN out on PORTC.5

'----- setup DT instant interrupt TMR1
INCLUDE "DT_INTS-14.bas" ; Base Interrupt System
INCLUDE "ReEnterPBP.bas" ; Include if using PBP interrupts (cf. asm interrupts)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, ReloadTMR1, ASM, no ; MUST be first
INT_Handler TMR1_INT, _T1handler, PBP, yes
endm .
INT_CREATE ; Creates the interrupt processor
ENDASM
@Freq = 6000 ; interrupt frequency, 1.7msec period gives 600Hz screen sweep
@Prescaler = 1 ; Timers Prescaler setting
T1CON = $00 ; $30 = Prescaler 1:8, TMR1 OFF ; $00=1:1, $10=1:2, $20=1:4, $30=1:8 -- NB: T1CON Must match @Prescaler value
@ INT_ENABLE TMR1_INT ; enable Timer 1 interrupts
GOSUB StartTimer ; Start the Timer

'------- Main
while 1=1 'loop forever
wend

;____This routine is Called on each TMR1 Interrupt____________________________
T1handler:
ISR:
duty_reg=duty_reg+1
if duty_reg>140 then duty_reg=0 'increment duty_reg each interrupt for ramp function
trisc.5=1
CCP1CON.4=duty_reg.0 '10bit PWM
CCP1CON.5=duty_reg.1
tempword=duty_reg & %1111111100
CCPR1L=tempword>>2
trisc.5=0
HIGH PORTC.4 'TEMP oscope timing pulse, retained to preserve timing
PAUSEUS 10
LOW PORTC.4
@ INT_RETURN

;---[TMR1 reload - interrupt handler]-----------------------------------------
ASM ; Calculate Timer Reload Constant
ReloadInst = 8 ; # of Intructions used to reload timer
if ((Prescaler == 1)||(Prescaler == 2)||(Prescaler == 4)||(Prescaler == 8))
MaxCount = 65536 + (ReloadInst / Prescaler)
TimerReload = MaxCount - (OSC*1000000/4/Prescaler/Freq)
if ((TimerReload < 0) || (TimerReload > (65535-ReloadInst)))
error Invalid Timer Values - check"OSC", "Freq" and "Prescaler"
endif
else
error Invalid Prescaler
endif
ENDASM

@Timer1 = TMR1L ; map timer registers to a word variable
Timer1 VAR WORD EXT
TimerReload CON EXT ; Get the External Constant
TMR1ON VAR T1CON.0 ; Alias the Timers ON/OFF bit

;---Reload Timer1------
ASM
ReloadTMR1
MOVE?CT 0, T1CON, TMR1ON ; 1 stop timer
MOVLW LOW(TimerReload) ; 1 Add TimerReload to the
ADDWF TMR1L,F ; 1 value in Timer1
BTFSC STATUS,C ; 1/2
INCF TMR1H,F ; 1
MOVLW HIGH(TimerReload) ; 1
ADDWF TMR1H,F ; 1
MOVE?CT 1, T1CON, TMR1ON ; 1 start timer
INT_RETURN
ENDASM

;---Start/Stop controls -----
StartTimer:
Timer1 = TimerReload ; Load Timer
TMR1ON = 1 ; start timer
RETURN

StopTimer:
TMR1ON = 0 ; stop timer
RETURN

END

Acetronics2
- 16th August 2010, 18:06
Hi, Gmg

You could use another Pic pin for "slamming" down the descending part of the signal : input when rising, ouput low when descending.

may be also use a slamming buffer with a BS170 i.e. ... ( ~ 3 ohms Rds on )

just an idea ;)

signal is pretty clean for a PWM filtered generator ... :)

Alain

timmers
- 17th August 2010, 09:35
Why not do it in analogue?
An RC circuit and a couple of digi-pots would do it. To get a linear ramp use a high-ish target voltage for the capacitor, say 10x the trip voltage (15v).
For a CRT timebase it must be free of any harmonics.

gmglickman
- 17th August 2010, 17:36
Alain,

Interesting idea. Do you mean using another pin to sink the filtered output after reaching the maximum voltage? I'll have to try that to see how it sharpens the drop.

Timmers,

Because I know even less about analog circuits than digital ones. You know: "when the only tool you have is a hammer, everything looks like a nail."

Acetronics2
- 17th August 2010, 17:40
Alain,

Interesting idea. Do you mean using another pin to sink the filtered output after reaching the maximum voltage? I'll have to try that to see how it sharpens the drop.


Exactly GmG ...

even use a small mosfet to get a sharper edge ... ( BS170 or 2N7000 )

Alain