PDA

View Full Version : Convert Rotary Encoder Code from 16F628A to 16F1825



RossWaddell
- 31st May 2012, 02:00
I did some prototyping with the 16F628A but now I need to move my code to another PIC which as at least two CCPs so I can send out HPWM commands to 2 motors (but use 1 rotary encoder to control the duty cycle). The 16F628A only has 1 CCP so after some trial and error on the Microchip site to find an appropriate PIC I settled on the 16F1825 which has 4 CCPs (more than I need, I know).

Now that I'm converting the code I'm wondering if I've really messed up. In the 16F628A data sheet it says that RB4/5 pins can be used as interrupt-on-pin change and indeed when I connect the A/B pins of the rotary encoder to these two and using the code below with Darrel's interrupt plug-in all works fine:



'************************************************* ***************
'* Name : Nacelle_Motors_16F628A.BAS *
'* Author : Ross A. Waddell *
'* Notice : Copyright (c) 2010 RAW Studios *
'* : All Rights Reserved *
'* Date : 05/18/2012 *
'* Version : 1.0 *
'* Notes : Motor control for TOS E engines *
'* : *
'************************************************* ***************


'---------Initialization--------

DEFINE OSC 20 ' set oscillator 20Mhz


' ************************************************** *************
' Device Fuses
' ************************************************** *************


@ __config _HS_OSC & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BODEN_ON & _LVP_OFF & _CP_OFF & _CPD_OFF




CMCON = 7 ' Turn off comparators
TRISA = %00000000 ' Make all PortA pins output
TRISB = %00110000 ' Make PortB pins 4-5 input




Old_Bits VAR BYTE
New_Bits VAR BYTE
RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortB.5;
' Rot Enc pin B connected to PortB.4
Old_RPM VAR BYTE
I VAR BYTE


'************************************************* **************************
' SETUP YOUR LCD HERE!!!
'************************************************* **************************


;Define LCD_DREG PORTA
;Define LCD_DBIT 0
;Define LCD_RSREG PORTA
;define LCD_RSBIT 4
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 7 ' Use PortB.7 as the Enable (E) bit since PortB.3
' on the 16F628A is the one-and-only HPWM output
;define LCD_BITS 4
;define LCD_LINES 2
;define LCD_COMMANDUS 2000
;define LCD_DATAUS 50


'---------Includes----------


INCLUDE "EE_Vars.PBP" ' Requires MPASM assembler
' Go to "View > Compile and Program Options..."
' On "Assembler" tab, check "Use MPASM"
' (no need to change any of the default settings)
' --> copy file to PBP main folder (i.e. c:\pbp)


MotorRPM VAR BYTE : @ EE_var _MotorRPM, BYTE, 50


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler RBC_INT, _Rot_Encoder, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


@ INT_ENABLE RBC_INT ;RB Port Change Interrupt


' Set default values
Old_Bits = PORTB & (%11110000)
Old_RPM = MotorRPM


LCDOUT $FE, 1
PAUSE 1000


rpmAdj CON 20 ' PWM cycle seems to be "higher" than HPWM, so need
' to reduce PWM cycle max to be less than HPWM cycle value


' Spin up motor to saved value of _MotorRPM
IF MotorRPM > rpmAdj Then
FOR i = 0 to (MotorRPM - rpmAdj)
PWM PortB.3, I, 100
NEXT I
EndIf
' Begin background HPWM
GOSUB motorhpwm


Main:
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB motorhpwm
@ EE_write_var _MotorRPM ; save the new number to EEPROM
EndIF


LCDOUT $FE, 2, "MotorRPM: ", #MotorRPM, " "
pause 10

GOTO Main


motorhpwm:
HPWM 1, MotorRPM, 20000 ; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing


RETURN
end


'---[RBC - interrupt handler]---------------------------------------------------
Rot_Encoder:
New_Bits = PORTB & (%11110000)
IF (New_Bits & %00110000) = (Old_Bits & %00110000) Then DoneRotEnc
RotEncDir = New_Bits.5 ^ Old_Bits.4
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF


DoneRotEnc:
Old_Bits = New_Bits
@ INT_RETURN




Now that I've got the 16F1825 chip, though, and I'm reading through the data sheet I don't see similar info about any of the PortA/C pins in terms of providing interrupt-on-pin change, so here's my question: have I picked the wrong chip? All I really want is one very similar to the 16F628A but with two CCPs. If I still can use the 16F1825, what does this code become and what pins can I use (presuming RA4/5 are for the external oscillator & RC3/5 are for the HPWM)?



ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler RBC_INT, _Rot_Encoder, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


@ INT_ENABLE RBC_INT ;RB Port Change Interrupt

HenrikOlsson
- 31st May 2012, 06:31
Hi,
Should work....from section 13 (Interrupt-On-Change) of the datasheet (http://ww1.microchip.com/downloads/en/DeviceDoc/41440B.pdf):

The PORTA pins can be configured to operate as Interrupt-On-Change (IOC) pins. On the PIC16(L)F1829 devices, the PORTB pins can also be configured to operate as IOC pins.

/Henrik.

RossWaddell
- 31st May 2012, 13:41
Thanks Henrik, but the 14 pin 16F1825 schematic doesn't show PortB pins.

HenrikOlsson
- 31st May 2012, 13:47
No, but it does show PortA pins and according to the quote from the datasheet PortA has IOC.

RossWaddell
- 31st May 2012, 23:56
Thanks Henrik - I didn't catch the part about PortB on chip 16F1829.

I've got the code converted to work with a 16F1825 but I had to add the following to the p16F1825.inc file in MPASM:



CCPTMRS EQU H'029E'
CCPTMRS0 EQU H'029E'


Can anyone tell me why? All I'm trying to do is use HPWM on CCP3 with the following config/code:



ANSELA = %00000000
ANSELC = %00000000
TRISA = %00000011 ' Make PortA pins 0-1 input for rotary encoder
TRISC = %00000000 ' Make all pins on PortC output

HPWM 3, 97, 20000


Until I added the line above in the p16F1825.inc MPASM file, I got this compile error:



Error[113] c:\pbp\pbppi14e.lib 2089 : Symbol not previously defined (CCPTMRS0)

Darrel Taylor
- 1st June 2012, 00:06
In the version history for PBP3 ... http://support.melabs.com/content/29-PicBasic-Pro-Version-History

One of the bug fixes listed is ... Fixed HPWM compile error for Enhanced Mid-Range parts in which CCPTMRS0 doesn't exist.

RossWaddell
- 1st June 2012, 01:31
Ah! So what you're saying is, use PBP3 already :) I did purchase the software upgrade but haven't tried converting my existing PBP code yet. Guess I should start sooner than later.

RossWaddell
- 1st June 2012, 03:11
This code works on a PIC 16F628A:



'---------Initialization--------

DEFINE OSC 20 ' set oscillator 20Mhz


' ************************************************** *************
' Device Fuses
' ************************************************** *************


#CONFIG
__config _HS_OSC & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BODEN_ON & _LVP_OFF & _CP_OFF & _CPD_OFF
#ENDCONFIG


CMCON = 7 ' Turn off comparators
TRISA = %00000000 ' Make all PortA pins output
TRISB = %00110000 ' Make PortB pins 4-5 input




Old_Bits VAR BYTE
New_Bits VAR BYTE
RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortB.5;
' Rot Enc pin B connected to PortB.4
Old_RPM VAR BYTE
I VAR BYTE


'************************************************* **************************
' SETUP YOUR LCD HERE!!!
'************************************************* **************************


;Define LCD_DREG PORTA
;Define LCD_DBIT 0
;Define LCD_RSREG PORTA
;define LCD_RSBIT 4
DEFINE LCD_EREG PORTB
DEFINE LCD_EBIT 7 ' Use PortB.7 as the Enable (E) bit since PortB.3
' on the 16F628A is the one-and-only HPWM output
;define LCD_BITS 4
;define LCD_LINES 2
;define LCD_COMMANDUS 2000
;define LCD_DATAUS 50


'---------Includes----------


INCLUDE "EE_Vars.PBP" ' Requires MPASM assembler
' Go to "View > Compile and Program Options..."
' On "Assembler" tab, check "Use MPASM"
' (no need to change any of the default settings)
' --> copy file to PBP main folder (i.e. c:\pbp)


MotorRPM VAR BYTE : @ EE_var _MotorRPM, BYTE, 50


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler RBC_INT, _Rot_Encoder, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


@ INT_ENABLE RBC_INT ;RB Port Change Interrupt


' Set default values
Old_Bits = PORTB & (%00110000)
Old_RPM = MotorRPM


LCDOUT $FE, 1
PAUSE 1000


rpmAdj CON 20 ' PWM cycle seems to be "higher" than HPWM, so need
' to reduce PWM cycle max to be less than HPWM cycle value


' Spin up motor to saved value of _MotorRPM
IF MotorRPM > rpmAdj Then
FOR i = 0 to (MotorRPM - rpmAdj)
PWM PortB.3, I, 100
NEXT I
EndIf
' Begin background HPWM
GOSUB motorhpwm


Main:
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB motorhpwm
@ EE_write_var _MotorRPM ; save the new number to EEPROM
EndIF


LCDOUT $FE, 2, "MotorRPM: ", #MotorRPM, " "
pause 10

GOTO Main


motorhpwm:
HPWM 1, MotorRPM, 20000 ; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing


RETURN
end


'---[RBC - interrupt handler]---------------------------------------------------
Rot_Encoder:
New_Bits = PORTB & (%00110000)
IF (New_Bits & %00110000) = (Old_Bits & %00110000) Then DoneRotEnc
RotEncDir = New_Bits.5 ^ Old_Bits.4
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF


DoneRotEnc:
Old_Bits = New_Bits
@ INT_RETURN


This code does NOT on a PIC 16F1825:



' ************************************************** *************
' Initialization
' ************************************************** *************

DEFINE OSC 20 ' Set oscillator 20Mhz


' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite
#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _LVP_OFF
#ENDCONFIG


ANSELA = %00000000
ANSELC = %00000000
TRISA = %00000011 ' Make PortA pins 0-1 input for rotary encoder
TRISC = %00000000 ' Make all pins on PortC output


Old_Bits VAR BYTE
New_Bits VAR BYTE
RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortB.5;
' Rot Enc pin B connected to PortB.4
Old_RPM VAR BYTE
I VAR BYTE


' ************************************************** *************
' SETUP YOUR LCD HERE!!!
' ************************************************** *************


;Define LCD_DREG PORTA
;Define LCD_DBIT 0
;Define LCD_RSREG PORTA
;define LCD_RSBIT 4
;DEFINE LCD_EREG PORTB
;DEFINE LCD_EBIT 7 ' Use PortB.7 as the Enable (E) bit since PortB.3
' on the 16F628A is the one-and-only HPWM output
;define LCD_BITS 4
;define LCD_LINES 2
;define LCD_COMMANDUS 2000
;define LCD_DATAUS 50


'---------Includes----------


INCLUDE "EE_Vars.PBP" ' Requires MPASM assembler
' Go to "View > Compile and Program Options..."
' On "Assembler" tab, check "Use MPASM"
' (no need to change any of the default settings)
' --> copy file to PBP main folder (i.e. c:\pbp)


MotorRPM VAR BYTE : @ EE_var _MotorRPM, BYTE, 50


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler IOC_INT, _Rot_Encoder, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


@ INT_ENABLE IOC_INT ; Interrupt-on-Change interrupt


' Set default values
Old_Bits = PORTA & (%00000011)
Old_RPM = MotorRPM


;LCDOUT $FE, 1
;PAUSE 1000


' Spin up motor to saved value of _MotorRPM
IF MotorRPM > 1 THEN
FOR i = 0 to MotorRPM
HPWM 3, I, 20000
NEXT I
EndIf


Main:
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB motorhpwm
@ EE_write_var _MotorRPM ; save the new number to EEPROM
EndIF


; LCDOUT $FE, 2, "MotorRPM: ", #MotorRPM, " "
; pause 10

GOTO Main


motorhpwm:
HPWM 3, MotorRPM, 20000 ; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing


RETURN
end


' ************************************************** *************
' [RBC - interrupt handler]
' ************************************************** *************
Rot_Encoder:
New_Bits = PORTA & (%00000011)
IF (New_Bits & %00000011) = (Old_Bits & %00000011) Then DoneRotEnc
RotEncDir = New_Bits.1 ^ Old_Bits.0
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF


DoneRotEnc:
Old_Bits = New_Bits
@ INT_RETURN


(The motor spins at full speed and encoder changes are ignored).

I've been at this most of the night - any ideas? Apart from the subtle change to spinning up the motor to the saved _MotorRPM EEPROM variable, I can't see what I'm doing wrong. Am I missing a register setting to tell it to use PWM on CCP3?

RossWaddell
- 1st June 2012, 19:30
I'll double check the connections, but can someone please confirm if I need this to enable the interrupt-on-change feature?



INTCON.3 = 1 'IOCIE bit set to 1

RossWaddell
- 1st June 2012, 19:55
That doesn't explain why the HPWM doesn't work, though. Is there something that needs to be configured for PWM use of CCP3?

Darrel Taylor
- 1st June 2012, 23:54
Ross,

The IOC interrupts work differently than the RBC interrupts on the 628A.

To fully understand them, you'll need to read section 13.0 INTERRUPT-ON-CHANGE in the datasheet.
But essentially each IOC pin operates independantly, with individual selection of rising/falling edges, and individual interrupt flags.
Each pin must be enabled with edge selects, and the flags must be cleared on each interrupt.

The registers you need to look at are ...

IOCAP: INTERRUPT-ON-CHANGE PORTA POSITIVE EDGE REGISTERI
IOCAN: INTERRUPT-ON-CHANGE PORTA NEGATIVE EDGE REGISTER
IOCAF: INTERRUPT-ON-CHANGE PORTA FLAG REGISTER

After being setup properly, you can use the interrupt flags to tell what pins changed, instead of reading the port and doing the newbits-oldbits stuff.

If you don't clear the flags, you end up in a continuous interrupt loop and none of the rest of your program will execute.

RossWaddell
- 2nd June 2012, 01:10
Thanks Darrel. IOC is definitely more complicated than the RBC interrupt but it's always good to learn new things :)

This is the config I have so far:



ANSELA = %00000000 ' Digital only (PortA)
ANSELC = %00000000 ' Digital only (PortC)
TRISA = %00000011 ' Make PortA pins 0-1 input for rotary encoder
TRISC = %00000000 ' Make all pins on PortC output


INTCON.3 = 1 ' Enable interrupt-on-change (IOCIE)
IOCAP.0 = 1
IOCAP.1 = 1
IOCAN.0 = 1
IOCAN.1 = 1
IOCAF.0 = 0
IOCAF.1 = 0


Are you saying that for my PBP interrupt routine I can change the code to look at IOCAF.0 and IOCAF.1 values instead of the NewBits/OldBits? The values on A/B from the rotary encoder (incremental quadrature) are:

CW
00
10
11
01

CCW
00
01
11
10

I think I still need to track the old values on A/B pins (RA1 & RA0 in my application) and do something like this pseudo code:

If A caused the interrupt then
Compare A & B
If different then
CW rotation
Else
CCW rotation
End If
End If

If B caused the interrupt then
Compare A & B
If same then
CW rotation
Else
CCW rotation
End If
End If

Does that make sense?

RossWaddell
- 2nd June 2012, 02:01
(I realize I'm jumping around a bit here but I didn't anticipate that switching from a 16F628A to a 16F1825 (in order to have 2 CCPs) was going to be so complicated)

First things first - getting the rotary encoder code to alter the PWM frequency doesn't matter if I can't get the HPWM command to work at all. In this thread (http://www.picbasic.co.uk/forum/showthread.php?t=1770), it seems to infer that HPWM will only work with channels 1 & 2, not 3 (and greater) and hence manually coding of all the applicable registers is needed. Is that true?

Darrel Taylor
- 3rd June 2012, 21:34
Do you have the latest version of PBP3 (3.0.5.x)?
Sorry if I've already asked, but I have to ask so many people to update I forget who I've asked.

I don't have a problem getting HPWM to work in the simulator.
I know that doesn't mean it will work on real hardware, but I won't have access to an 1825 till monday.


For the encoder.
When using IOC_INT's, determining the direction is as simple as ...

DIR = (A ^ B) ^ IOCAF.0
Which is effectively the same thing as your pseudo code.

RossWaddell
- 3rd June 2012, 22:15
I found this in the c:\pbp3\readme.txt file:

Release Notes:
-------------------------------------3.0.5--------------------------------------
Fixed BLOCK_SIZE for 16(L)F1826/1827
Fixed configuration defaults for 12(L)F752 to match MPASM 5.44
Legacy SFR names restored for many devices
New Parts: 12F1501, 16F1503, 16F1508, 16F1509, 16F1512, 16F1513

... so I'm assuming that means I have 3.0.5 (I only bought the PBP3 upgrade last month).

I'll be using HPWM with a motor, but right now I just want to see it working with an LED. I've tried using CCP1 (which is an EECP) and CCP3, but no luck; the LED is just dimly lit no matter what Duty Cycle I use:



DEFINE OSC 20 ' Set oscillator 20Mhz


' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite
#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _LVP_OFF
#ENDCONFIG




DEFINE CCP3_REG PORTA
DEFINE CCP3_BIT 2


ANSELA = %00000000 ' Digital only (PortA)
ANSELC = %00000000 ' Digital only (PortC)
ADCON1 = $0F
TRISA = %00000000 ' Make all pins on PortA output
TRISC = %00000000 ' Make all pins on PortC output


DUTY3 var word


' Set CCP modules to PWM mode
CCP3CON = %00001100 ' Mode select = PWM


PR2 = $FF '255
T2CON = %00000110 ' TMR2 on 1:!6 prescale


DUTY3 = 512
CCP3CON.4 = DUTY3.0
CCP3CON.5 = DUTY3.1
CCPR3L = DUTY3 >> 2


lblLoop:






goto lblLoop


end


(note that I used the example from the link above, but I'd really like to just use HPWM as my interrupt routine is going to modify the motor's speed via the HPWM duty cycle and that worked really well with the 16F628A)

Dave
- 4th June 2012, 12:13
Ross Waddell, Here is a test program I put together a few months back for trying out an 16F1825 using the PWM outputs and the steering logic. Just connect a pot and look at the outputs....

Darrel Taylor
- 4th June 2012, 18:40
Ross,

I still don't have any problems using HPWM on a real 16F1825.

Two suggestions...

Change your CONFIG2 line to ...

__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF

The PLL is enabled by default. You have to specifically disable it.

And if you are using an melabs programmer, go to Options > More Options > Set Options to Defaults.
That will make sure everything is being programmed properly.

RossWaddell
- 4th June 2012, 20:22
I'll try out your suggestions tonight when I get home, Darrel - thanks.

Just to confirm: you used code like this?




DEFINE OSC 20 ' Set oscillator 20Mhz

#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

ANSELA = %00000000 ' Digital only (PortA)
ANSELC = %00000000 ' Digital only (PortC)
ADCON1 = $0F
TRISA = %00000000 ' Make all pins on PortA output
TRISC = %00000000 ' Make all pins on PortC output


' Set CCP modules to PWM mode
CCP3CON = %00001100 ' Mode select = PWM

HPWM 3, 127, 20000

lblLoop:
Pause 1

GoTo lblLoop

End

Darrel Taylor
- 4th June 2012, 22:01
Your's should do something, but here's what I'm using.


DEFINE OSC 20
#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG

DEFINE CCP3_REG PORTA
DEFINE CCP3_BIT 2

DUTY VAR BYTE

ANSELA.2 = 0

Main:
FOR Duty = 0 TO 255
PAUSE 10
HPWM 3, Duty, 20000
NEXT Duty
GOTO Main

RossWaddell
- 4th June 2012, 22:08
Thanks Darrel!

RossWaddell
- 5th June 2012, 00:04
It works! I think I had a combination of problems - PBP config and a weird breadboard connection issue (I have 4 banks of +/- and when the chip was connected to the far bank the voltage potential between Vdd and Vss wasn't 4.91 volts (my 7805's 5v) - more like 1.93 v. Once I configured the chip with Darrel's code above and move the power connections to one of the other banks I can see the LED fade in and repeat.

Thanks heaps Darrel!

RossWaddell
- 5th June 2012, 02:44
Now that I've got the HPWM stuff sorted (thanks to Darrel) I'm back to the original problem which is converting RBC interrupts to IOC on the 16F1825 - rotating the rotary encoder knob has no effect on MotorRPM. Here's the code I have so far:



DEFINE OSC 20 ' Set oscillator 20Mhz


' ************************************************** *************
' Device Fuses
' ************************************************** *************
' PIC chip data sheets can be found here: C:\Program Files\Microchip\MPASM Suite


#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG


' ************************************************** *************
' Initialization
' ************************************************** *************


DEFINE CCP3_REG PORTA
DEFINE CCP3_BIT 2


;DEFINE CCP4_REG PORTC
;DEFINE CCP4_BIT 1




ANSELA.2 = 0 ' Digital only on CCP3 pin
;ANSELC.1 = 0 ' Digital only on CCP4 pin
TRISA = %00000011 ' Make PortA pins 0-1 input for rotary encoder
;TRISC = %00000000 ' Make all pins on PortC output


;INTCON.3 = 1 ' Enable interrupt-on-change (IOCIE)
INTCON = %10001000 ' Global int enabled, IOCI enabled, IOCI flag bit 0 clr
IOCAP.0 = 1 ' Enable positive (rising edge) change
IOCAP.1 = 1 ' Enable positive (rising edge) change
IOCAN.0 = 1 ' Enable negative (falling edge) change
IOCAN.1 = 1 ' Enable negative (falling edge) change
IOCAF.0 = 0 ' Clear interupt-on-change flag
IOCAF.1 = 0 ' Clear interupt-on-change flag




A VAR PortA.1 ' Variable to store encoder's A pin output (RA1)
B VAR PortA.0 ' Variable to store encoder's B pin output (RA0)


RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortA.1;
' Rot Enc pin B connected to PortA.0
Old_RPM VAR BYTE
I VAR BYTE




' ************************************************** *************
' SETUP YOUR LCD HERE!!!
' ************************************************** *************


;Define LCD_DREG PORTA
;Define LCD_DBIT 0
;Define LCD_RSREG PORTA
;define LCD_RSBIT 4
;DEFINE LCD_EREG PORTB
;DEFINE LCD_EBIT 7
;define LCD_BITS 4
;define LCD_LINES 2
;define LCD_COMMANDUS 2000
;define LCD_DATAUS 50


' ************************************************** *************
' Includes
' ************************************************** *************


INCLUDE "EE_Vars.PBP" ' Requires MPASM assembler
' Go to "View > Compile and Program Options..."
' On "Assembler" tab, check "Use MPASM"
' (no need to change any of the default settings)
' --> copy file to PBP main folder (i.e. c:\pbp)


MotorRPM VAR BYTE : @ EE_var _MotorRPM, BYTE, 100


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler IOC_INT, _Rot_Encoder, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


@ INT_ENABLE IOC_INT ; Interrupt-on-Change interrupt


' Set default values
Old_RPM = MotorRPM


;LCDOUT $FE, 1
;PAUSE 1000


' Spin up motor to saved value of _MotorRPM
IF MotorRPM > 1 THEN
FOR I = 0 to MotorRPM
pause 30
HPWM 3, I, 20000
; HPWM 4, I, 20000
NEXT I
EndIf


Main:
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB motorhpwm
@ EE_write_var _MotorRPM ; save the new number to EEPROM
EndIF


; LCDOUT $FE, 2, "MotorRPM: ", #MotorRPM, " "
; pause 10

GOTO Main


motorhpwm:
HPWM 3, MotorRPM, 20000 ; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing
; HPWM 4, MotorRPM, 20000 ; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing


RETURN
end


' ************************************************** *************
' [RBC - interrupt handler]
' ************************************************** *************
Rot_Encoder:
RotEncDir = (A ^ B) ^ IOCAF.0
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF


IOCAF.0 = 0 ' Clear interrupt flags
IOCAF.1 = 0


@ INT_RETURN


Any ideas?

Darrel Taylor
- 5th June 2012, 05:08
Not bad Ross,

All you need is to clear ANSELA bits 0 and 1.

RossWaddell
- 5th June 2012, 13:02
That worked! At least as recognizing the IOC, but I think my logic is wrong - turning the knob one way makes the motor go faster and then slower. The idea is that a CW rotation would make it go faster but turning it CCW would slow it down:



' ************************************************** *************
' [RBC - interrupt handler]
' ************************************************** *************
Rot_Encoder:
RotEncDir = (A ^ B) ^ IOCAF.0
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF


IOCAF.0 = 0 ' Clear interrupt flags
IOCAF.1 = 0


@ INT_RETURN

RossWaddell
- 6th June 2012, 01:59
If I revert to the Old_Bits/New_Bits logic it now works:



DEFINE OSC 20 ' Set oscillator 20Mhz


' ************************************************** *************
' Device Fuses
' ************************************************** *************
#CONFIG
__config _CONFIG1, _FOSC_HS & _WDTE_ON & _PWRTE_ON & _MCLRE_OFF & _CP_OFF & _CPD_OFF
__config _CONFIG2, _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LVP_OFF
#ENDCONFIG


' ************************************************** *************
' Initialization
' ************************************************** *************


DEFINE CCP3_REG PORTA
DEFINE CCP3_BIT 2


ANSELA.0 = 0 ' Digital only on roatary encoder B pin
ANSELA.1 = 0 ' Digital only on roatary encoder A pin
ANSELA.2 = 0 ' Digital only on CCP3 pin
TRISA = %00000011 ' Make PortA pins 0-1 input for rotary encoder


INTCON = %10001000 ' Global int enabled, IOCI enabled, IOCI flag bit 0 clr
IOCAP.0 = 1 ' Enable positive (rising edge) change
IOCAP.1 = 1 ' Enable positive (rising edge) change
IOCAN.0 = 1 ' Enable negative (falling edge) change
IOCAN.1 = 1 ' Enable negative (falling edge) change
IOCAF.0 = 0 ' Clear interupt-on-change flag
IOCAF.1 = 0 ' Clear interupt-on-change flag


Old_Bits VAR BYTE
New_Bits VAR BYTE
RotEncDir VAR BIT ' 1=CW, 0=CCW
' Rot Enc pin A connected to PortA.1;
' Rot Enc pin B connected to PortA.0
Old_RPM VAR BYTE
I VAR BYTE


' ************************************************** *************
' Includes
' ************************************************** *************


INCLUDE "EE_Vars.PBP" ' Requires MPASM assembler
' Go to "View > Compile and Program Options..."
' On "Assembler" tab, check "Use MPASM"
' (no need to change any of the default settings)
' --> copy file to PBP main folder (i.e. c:\pbp)


MotorRPM VAR BYTE : @ EE_var _MotorRPM, BYTE, 100
;PortNacelleDir VAR BYTE : @ EE_var _PortNacelleDir, BYTE, 0 ' 0 = CCW (inwards)
' 1 = CW (outwards)


INCLUDE "DT_INTS-14.bas" ' Base Interrupt System
INCLUDE "ReEnterPBP.bas" ' Include if using PBP interrupts
' --> copy both files to PBP main folder
' (i.e. c:\pbp)
ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler IOC_INT, _Rot_Encoder, PBP, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM


@ INT_ENABLE IOC_INT ; Interrupt-on-Change interrupt


' Set default values
Old_RPM = MotorRPM
Old_Bits = PORTA & (%00000011)


' Spin up motor to saved value of _MotorRPM
IF MotorRPM > 1 THEN
FOR I = 0 to MotorRPM
pause 30
HPWM 3, I, 20000
NEXT I
EndIf


Main:
IF MotorRPM <> Old_RPM Then
Old_RPM = MotorRPM
GOSUB motorhpwm
@ EE_write_var _MotorRPM ; save the new number to EEPROM
EndIF

GOTO Main


motorhpwm:
HPWM 3, MotorRPM, 20000 ; Tried 245 Hz but it made the motor too loud.
; Supposedly, anything above 20kHz is above
; human hearing


RETURN
end


' ************************************************** *************
' [IOC - interrupt handler]
' ************************************************** *************
Rot_Encoder:
New_Bits = PORTA & (%00000011)
IF (New_Bits & %00000011) = (Old_Bits & %00000011) Then DoneRotEnc
RotEncDir = New_Bits.1 ^ Old_Bits.0
IF RotEncDir = 1 Then
; CW rotation - increase speed but only to a max of 255
IF MotorRPM < 255 then MotorRPM = MotorRPM + 1
Else
' CCW rotation - decrease speed to a min of 0
IF MotorRPM > 0 Then MotorRPM = MotorRPM - 1
EndIF


DoneRotEnc:
Old_Bits = New_Bits

IOCAF.0 = 0 ' Clear interrupt flags
IOCAF.1 = 0


@ INT_RETURN

Darrel Taylor
- 6th June 2012, 20:43
Yay!

Well, stick with what works for you then.

Cheers.

RossWaddell
- 6th June 2012, 21:22
Thanks Darrel. I would have preferred your approach as its the more elegant solution, but oh well ...

RossWaddell
- 7th June 2012, 04:34
(One last somewhat general question)

If I'm using an external 20Mhz oscillator connected to the CLKIN/CLKOUT pins (RA5 & RA4, respectively) should I set their TRISA value to 1? Right now I have this:



TRISA = %00000011 ' Make PortA pins 0-1 input for rotary encoder


Should that be:



TRISA = %00011011 ' Make PortA pins 0-1 input for rotary encoder, 4-5 for external oscillator