I couldn't help myself, had to see what I could do with just some simple, basic (no pun intended) coding. No interrupts, no hardware assist, no inline assembly language, just 33 line loop of straight PBP.
On an 18F4520 running at 20MHz it's seems solid as a rock at 20kHz. Above 20kHz it misses the odd count when subjected to long bursts. This is with a function generetor generating bursts of "perfect" signals with 50% dutycyle so in real life it might be a little less.
Connecting an optical encoder works fine but a mechanical one does not due to contact bounce. Using a 16bit WORD for the position counter and simple 1x decoding, pissing away 3/4 of the resoultion but that was what was asked for.
It bit-bangs SPI data to an AD9833 but there's no math to calculate actual frequency.
The LCD code is there for my debug purposes but I left it there just in case.
Code:
DEFINE LOADER_USED 1
DEFINE OSC 20
DEFINE LCD_DREG PORTD 'LCD data port
DEFINE LCD_DBIT 0 'LCD data starting bit 0 or 4
DEFINE LCD_RSREG PORTA 'LCD register select port
DEFINE LCD_RSBIT 3 'LCD register select bit
DEFINE LCD_EREG PORTA 'LCD enable port
DEFINE LCD_EBIT 1 'LCD enable bit
DEFINE LCD_RWREG PORTA 'LCD read/write port
DEFINE LCD_RWBIT 2 'LCD read/write bit
DEFINE LCD_BITS 8 'LCD bus size 4 or 8
DEFINE LCD_LINES 2 'Number lines on LCD
DEFINE LCD_COMMANDUS 3000 'Command delay time in us
DEFINE LCD_DATAUS 40 'Data delay time in us
FSYNC VAR LATB.3
SCLK VAR LATB.4
SDATA VAR LATB.5
Ch_A VAR PortB.6
Ch_B VAR PortB.7
Position VAR WORD ' Encoder position
Frequency VAR WORD '
AD9833 VAR WORD[5] ' Array for data to be sent to the AD9833
BitsToSend VAR BYTE ' Number of bits -1 to send to the AD9833 (max 127)
Ch_A_Old VAR BIT ' Memory bit for edge-detection
'-------------------------------------------------------------
'------------------------- Init ------------------------------
'-------------------------------------------------------------
CMCON = 7
ADCON1 = %00001111 ' No analog inputs.
TRISB = %11000001
Frequency = 0
Position = 0
BitsToSend = 255
PAUSE 1000
LCDOUT $FE, 1, "AD9833..."
' Initialize the AD9833 as per example in AN-1070.
' Just to verify that we're good to go.
' This should make it output 400Hz with a 25MHz base clock
AD9833[4] = $2100
AD9833[3] = $50C7
AD9833[2] = $4000
AD9833[1] = $C000
AD9833[0] = $2000
FSYNC = 0
For BitsToSend = 79 to 0 Step -1
SDATA = AD9833.0[BitsToSend]
SCLK = 0
PAUSEUS 20
SCLK = 1
PAUSEUS 20
NEXT
FSYNC = 1
Main:
'-------------------------------------------------------------
'--------------Poll encoder and keep count -------------------
'-------------------------------------------------------------
IF Ch_A = 1 THEN
IF Ch_A_Old = 0 THEN
IF Ch_B = 1 THEN
Position = Position + 1
ELSE
Position = Position - 1
ENDIF
Ch_A_Old = 1
ENDIF
ELSE
Ch_A_Old = 0
ENDIF
'-------------------------------------------------------------
'-------------------------------------------------------------
'------------- If needed, prepare to update DDS --------------
'-------------------------------------------------------------
IF Position <> Frequency THEN ' Has the count changed since last update of the DDS?
IF BitsToSend.7 THEN ' Are we free to update?
Frequency = Position
' See datasheet for AD9833, this is just for basic testing.
AD9833[2] = %0010000000000000
AD9833[1] = %0100000000000111
AD9833[0] = Frequency
AD9833.0[15] = 0
AD9833.0[14] = 1
BitsToSend = 47 ' 3*16 = 48 but we're using this variable to index the bits so minus 1
ENDIF
ENDIF
'-------------------------------------------------------------
'-------------------------------------------------------------
'---If DDS is to be updated, do it one bit per loop iteration----
'-------------------------------------------------------------
IF NOT BitsToSend.7 THEN ' Still bits left to send ?
FSYNC = 0 ' Chip select
SDATA = AD9833.0[BitsToSend] ' Setup data
SCLK = 0 ' Clock low
BitsToSend = BitsToSend - 1 ' Index next bit
SCLK = 1 ' Clock high
ELSE
SCLK = 1
FSYNC = 1 ' Chip select
ENDIF
' This is for debugging purposes.
' If button is pressed and LCD updated while the encoder is moved
' counts will obviously be missed.
IF PortB.0 = 0 THEN
LCDOUT $FE, 1, DEC Position, " ", DEC Frequency, " ", DEC BitsToSend
ENDIF
Goto Main
Bookmarks