pic pwm - hamstack.compic pwm 9/16/13!! ! ! ! ! ! ... these notes demonstrate generating pwm on the...

88
kj6hfr PIC PWM 9/16/13 page 1 of 88 PIC PWM © Robert Ralston KJ6HFR September 2013 These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. Most demonstrations can use the DEV-1 HamStack Development Board and some require it, using the LCD, encoder, and LEDs. PWM is generated using on-chip PWM hardware peripherals, the CCP (Capture/Compare/PWM) modules. Software: the free Swordfish compiler Special Edition, (ver. 2.2.1.6). Most current data sheet versions: DS39626E: PIC18F2525/2620/4525/4620 Data Sheet DS41412F: PIC18(L)F2X/4XK22 Data Sheet Data Sheet references look like this: (4620 DS pg. 141) (46K22 DS pg. 36)

Upload: buinguyet

Post on 31-Jan-2018

250 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 1 of 88

PIC PWM© Robert Ralston

KJ6HFRSeptember 2013

These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. Most demonstrations can use the DEV-1 HamStack Development Board and some require it, using the LCD, encoder, and LEDs.

PWM is generated using on-chip PWM hardware peripherals, the CCP (Capture/Compare/PWM) modules.

Software: the free Swordfish compiler Special Edition, (ver. 2.2.1.6).

Most current data sheet versions:

DS39626E: PIC18F2525/2620/4525/4620 Data Sheet DS41412F: PIC18(L)F2X/4XK22 Data Sheet

Data Sheet references look like this:

(4620 DS pg. 141) (46K22 DS pg. 36)

Page 2: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 2 of 88

My intention: read the data sheets and get the chip to “do something” with minimum code, and to understand Library files better such as the PWM.bas module on the Swordfish site. All code is “demonstration”, meaning that there is no error checking and certain Microchip data sheet guidelines are not followed, such as making sure that the very first PWM cycle is complete by waiting until an interrupt flag is set. I also use power-up values in registers rather than explicitly setting them.

When a control register is loaded, the number is displayed in binary so it’s easy to compare that register with the data sheets. Other registers are displayed in decimal.

CCP2CON = %00001100 // enable PWM modeCCPR2L = 128 // set duty cycle = 50%TRISC.1 = 0 // make pin 16 output 18F4620T2CON = %00000100 // enable Timer 2

There is some code explanation but it’s best to read the referenced data sheet.

Some 18F46K22 code requires “special handling” as the free version of Swordfish is not set to load values into 40 Special Function Registers (SFRs) not in Access Ram.

All Swordfish code in these notes assumes using the Include files and HamStack Library files from Sierra Radio Systems (see appendix for details).

My PICkit2 programmer would not recognize the 18F46K22 until it was upgraded with the latest device file (see appendix for details).

A Saleae Logic probe proved to be a valuable bench tool, both here and especially when sending I2C commands from the HamStack to an NXP PCA9685 16 channel, 12 bit PWM controller. That project (‘Dial-a-Color’), having 3 encoders each separately control red, green, and blue PWM to an RGB LED, started this project as I wondered if this same thing could be done on an 18F46K22, knowing that it could not on an 18F4620, at least in hardware.

Corrections are welcome.

Page 3: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 3 of 88

Page 4: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 4 of 88

61

63

64

75

84

86

Code marked above for the DEV-1 board uses the rotary encoder, LCD, and LEDs but all code can be run with the HamStack mounted on the DEV-1 board.

Some 46K22 code cannot be re-written for the 4620 since the 46K22 has more capabilities (steering, slew rate control, more CCPs, ...)

http://www.youtube.com/watch?v=bDlDZJNax5I

Page 5: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 5 of 88

Basic PWM

{BasicPWM1: HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10

CCP2CON = %00001100 // enable PWM modeCCPR2L = 128 // set for 50% duty cycleTRISC.1 = 0 // pin 16 18F4620; scope this pinT2CON = %00000100 // enable Timer 2

While (TRUE)Wend

{BasicPWM2: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF // remove this line for 64 MHz oscillator

CCP2CON = %00001100 // enable PWM modeCCPR2L = 128 // set for 50% duty cycleTRISC.1 = 0 // pin 16 18F46K22; scope this pinT2CON = %00000100 // enable Timer 2

While (TRUE)Wend

Page 6: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 6 of 88

Note: On the DEV-1 HamStack Development Board C1 is connected to pin 1 of the PS2 keyboard jack, the bi-directional open collector keyboard data line.

On the DEV-1 HamStack Development Board C2 is connected to the LM386 audio amplifier.

Generating PWM on C2, the lower frequency (9766 Hz) can barely be heard through the speaker.

Changing to the code below will generate PWM on C2 (pin 17)

CCP1CON = %00001100CCPR1L = 128TRISC.2 = 0 // pin 17 18F4620/18F46K22; scope this pinT2CON = %00000100

CCP2CON (Capture/Compare 2 Control Register): Bits 2-3 are set to enable PWM mode (4620 DS pg. 141; 46K22 DS pg. 207).

CCPR2L (Capture/Compare/PWM Register 2 Low Byte, also called Duty Cycle Register): this byte value partly determines the duty cycle, how long C1 (pin 16) will be high. (4620 DS pg.146). A value of 128 generates a 50% duty cycle.

TRISC.1 sets pin C1 as output.

T2CON: (Timer 2 Control Register): setting bit 2 turns Timer 2 on, starting PWM (4620 DS pg.135).

The code above does not explicitly set the Timer2 Period Register (PR2) even though the value in this register partly determines the PWM period.

PR2 has a default power-up setting of 255 (4620 DS pg. 52).

Page 7: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 7 of 88

HamStack with 18F4620 and 10 MHz crystal:

! PWM Period = (255 + 1)(4)(.0000001)(1) = 102.4 µs (9766 Hz)! Pulse Width in time = (512)(.0000001)(1) = 51.2 µs! Duty Cycle Ratio = (512 / 1024) = .5 = 50%

HamStack with 18F46K22 and 16 MHz crystal (PLL off):

! Period = (255 + 1)(4)(0.0000000625)(1) = 64 µs (15625 Hz)! Pulse Width in time = (512)(.0000000625)(1) = 32 µs! Duty Cycle Ratio = (512 / 1024) = .5 = 50%

Period and Duty Cycle calculations ( screenshots from 46K22 DS pg. 187)

PR2 = 255 (default power-up value)Tosc = 1/10,000,000 = 0.0000001 (18F4620 running at 10 MHz)Tosc = 1/16,000,000 = 0.0000000625 (18F46K22 running at 16 MHz)TMR2 Prescale Value = 1

CCPR2L:CCP2CON<5:4> is a ten bit value.

The eight MSBs are the CCPR2L value, here 128 or 1000 0000 in binary. The remaining two LSBs are in CCP2CON and are 0 as a default power-up value.

Combining the 8 and 2 bits in this case = 10 000 0000 or 512 in decimal.

Page 8: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 8 of 88

Screen shots from a Saleae Logic probe on C1

HamStack with 18F46K22 and 16 MHz crystal (PLL on):

Period = (255 + 1)(4)(0.000000015625)(1) = 16 µs (62500 Hz) Pulse Width in time = (512) (.0000000625)(1) = 8 µs Duty Cycle Ratio = (512 / 1024) = .5 = 50%

Page 9: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 9 of 88

To minimize code, the previous examples used Timer2, the default power-up PWM Timer for both chips. Also, its Period Register, PR2, comes loaded with 255 on power-up.

The 2 CCP modules in the 18F4620 (CCP1, CCP2) must use Timer2 for PWM.

The 5 CCP modules in the 18F46K22 (CCP1, CCP2, CCP3, CCP4, CCP5) can use Timer2 (power-up default), Timer4, or Timer6.

CCP OUTPUT pins

18F4620: CCP1 C2 CCP2 C1 (or B3 via Config settings for CCP2MX)

18F46K22L CCP1 C2 CCP2 C1 (or B3 via Config settings for CCP2MX) CCP3 B5 (or E0 via Config settings for CCP3MX) CCP4 D1 CCP5 E2

Still using the default power-up Timer2 with its Period Register set for 255 on power-up, I thought the following code changes might work with the 18F46K22:

CCP3CON = %00001100 // enable CCP3 for PWMCCPR3L = 128 // set for 50% duty cycleTRISB.5 = 0 // pin 38 18F46K22T2CON = %00000100

OR

CCP4CON = %00001100 // enable CCP4 for PWMCCPR4L = 128 // set for 50% duty cycleTRISD.1 = 0 // pin 20 18F46K22T2CON = %00000100

OR

CCP5CON = %00001100 // enable CCP5 for PWMCCPR5L = 128 // set for 50% duty cycleTRISE.2 = 0 // pin 10 18F46K22T2CON = %00000100

Page 10: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 10 of 88

But no, none of them work.

The free version (SE or Special Edition) of Swordfish cannot load values into CCP3CON, CCPR3L, ..., CCPR5L without special handling.

An option setting and macro will solve this specific problem (for details see Appendix: SFR Fix).

This fix works for both CCP4 and CCP5 (CCP5 illustrated below), but not CCP3.

{BasicPWM3: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46k22Clock = 16Config PLLCFG = OFF

#option SWORDFISH_SE = truePublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro

write_sfr(CCP5CON, %00001100) // enable CCP5 for PWMwrite_sfr(CCPR5L, 128) // set for 50% duty cycleTRISE.2 = 0 // pin 10 18F46K22; scope this pin T2CON = %00000100

While (TRUE)Wend

Page 11: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 11 of 88

{BasicPWM4: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46k22Clock = 16Config pllcfg = off, CCP3MX = PORTE0

#option SWORDFISH_SE = truePublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro

write_sfr(CCP3CON, %00001100) // enable CCP3 for PWMwrite_sfr(CCPR3L, 128) // set for 50% duty cycleTRISE.0 = 0 // pin 8 18F46K22; scope this pin T2CON = %00000100

While (TRUE)Wend

The problem with CCP3 is that the default output pin, B5, is grounded on the HamStack board. I’m guessing that this pin, labelled PGM, is grounded “in order to prevent inadvertent entry into Programming mode” (Microchip Application Note AN910).

To achieve PWM with CCP3, we have to use both the macro and Config the output pin to E0.

Note: I think the 46K22 DS pg. 180 is mistaken when it states that the default output pin for CCP3 is E0. This configuration bit retains whatever value it has been programmed to through power cycles. But when the 18F46K22 is erased, the CCP3MX bit is set to 1, for B5.

Neither the Mecanique nor the Sierra Radio Systems 18F46K22. bas Include files explicitly have a default setting for CCP3MX.

Page 12: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 12 of 88

Duty Cycle

{DutyCycle1 HamStack with 18F4620; DEV-1 board optional}Device = 18F4620 Clock = 10

Dim index As Byte, UpDir As Booleanindex = 255

CCP2CON = %00001100 // enable PWM modeTRISC.1 = 0 // C1, 18F4620 pin 16; scope this pinT2CON = %00000100 // enable Timer2

While (TRUE) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf

If (UpDir) Then Inc(index) Else Dec(index) EndIf

CCPR2L = index // load duty cycle valueDelayMS(4) // delay to see on scope or LEDWend

The code above will cycle back and forth through 256 duty cycle values.

Connect an LED and a current limiting resistor between pin 16 and ground to cycle the LED , bright to dim and dim to bright.

C1

Page 13: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 13 of 88

{DutyCycle2 HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22 Clock = 16Config PLLCFG = OFF // clock chip at 16 MHz

Dim index As Byte, UpDir As Booleanindex = 255

CCP2CON = %00001100TRISC.1 = 0 // C1, 18F46K22 pin 16; scope this pinT2CON = %00000100

While (TRUE) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf

If (UpDir) Then Inc(index) Else Dec(index) EndIf

CCPR2L = index // load duty cycle valueDelayMS(4) // delay to see on scope or LEDWend

CCP1CON = %00001100TRISC.2 = 0 // C2, 18F4620/18F46K22 pin 17

CCPR1L = index // load duty cycle value

Change these 3 lines to generate PWM on C2 (pin 17); watch an LED and scope, and listen on the DEV-1 board: C2 is connected to the LM386 amplifier.

Page 14: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 14 of 88

Duty Cycle Calculations ( screenshot from 46K22 DS pg. 187)

In the previous code, when index = 0 and is loaded into CCPR2L, the duty cycle = 0% for either chip at any oscillator frequency. This depends on the power-up condition where the two lowest bits (CCPxCON<5:4>) are initialized to zero.

Duty Cycle Ratio = 0 / 1024 = 0 = 0%

When index = 255, the duty cycle is close to 100% for either chip at any oscillator frequency. Duty Cycle Ratio = 1020/1024 = 0.996 = 99.6%

The following code, by setting the two duty cycle LSBs, generates a duty cycle slightly higher than the 99.6% seen previously.

Duty Cycle Ratio = 1023/1024 = 0.999023 = 99.9%! ! Pulse Width in time = (1023)(.0000001)(1)! ! = 102.3 µs

{DutyCycle3 HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10

CCP2CON = %00111100 // Both duty cycle LSBs set CCPR2L = 255TRISC.1 = 0 // pin 16 18F4620; scope this pinT2CON = %00000100

While (TRUE)Wend

Page 15: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 15 of 88

{DutyCycle4 HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF

CCP2CON = %00111100 // Both duty cycle LSBs set CCPR2L = 255TRISC.1 = 0 // pin 16 18F46K22; scope this pinT2CON = %00000100

While (TRUE)Wend

The DutyCycle4 code above, by setting the two duty cycle LSBs, generates the highest possible duty cycle, not counting 100%, since the Timer has to count up every bit of the ten bits except one.

Duty Cycle Ratio = 1023/1024 = 0.999023 = 99.90%! ! Pulse Width in time = (1023)(0.0000000625)(1)!! = 63.93 µs

The period is 64 µs, so the off time is approximately 70 ns.

A Saleae Logic probe capture is shown below. The pulse shown is about 64 µs.

Page 16: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 16 of 88

Similarly, the following code, by setting one of the two duty cycle LSBs, generates a duty cycle slightly higher than the 0% seen previously, the shortest possible pulse under these conditions.

Duty Cycle Ratio = 1/1024 = 0.000976 = .097%! ! Pulse Width in time!= (1)(0.0000000625)(1)! ! = 62.5 ns

{DutyCycle5 HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF

CCP2CON = %00011100 // One duty cycle LSB set CCPR2L = 0 // 8 duty cycle MSBs not setTRISC.1 = 0 // pin 16 18F4620T2CON = %00000100

While (TRUE)Wend

A Saleae Logic probe at its highest acquisition speed has trouble capturing this 62.5 nanosecond pulse; the pulse width varied from about 45 to 84 ns.

Page 17: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 17 of 88

{DutyCycle6 HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10 DIM index As Wordindex = 0

CCP2CON = %00001100 // No duty cycle LSBs set CCPR2L = 0CCP1CON = %00001100 // No duty cycle LSBs set CCPR1L = 0

TRISC.1 = 0 // pin 16 18F4620; scope this pinTRISC.2 = 0 // pin 17 18F4620; scope this pinT2CON = %00000100

While (TRUE)CCP1CON.5 = index.1 // load duty cycle LSBs in CCP1CCP1CON.4 = index.0 // which controls C2

CCPR1L = index >> 2 // load duty cycle MSBs in CCP1CCPR2L = index >> 2 // load duty cycle MSBs in CCP2

DelayMS(700) //slow change way downInc(index)If (index >32) Then index = 0 EndIfWend

The following code generates PWM from both CCP1 (C2, pin 17) and CCP2 (C1, pin 16).

CCP2 has its duty cycle set only by the eight MSBs whereas CCP1 utilizes all ten duty cycle bits.

This difference between using the two LSBs and not using them can be seen on a dual channel scope (.5 µs / div).

Page 18: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 18 of 88

The code below uses the DEV-1 board’s rotary encoder, LCD, and LED4. Most of the encoder code is directly from the DEV-1 test program.

The encoder will crank the duty cycle up and down, displaying the value of the index variable on the LCD, from 0 to 1023.

Covering the full range requires slightly more than 51 full turns of the encoder.

The Config CCP2MX = portBE redirects the PWM from C1 to B3; hence the TRISB.3 statement so the dimming and brightening can be seen directly on the DEV-1 board’s LED4.

{DutyCycle7 HamStack with 18F4620; DEV-1 board}Device = 18F4620 Clock = 10Config CCP2MX = portBE // redirect PWM from C1 to B3 #option LCD_DATA = PORTD.0#option LCD_RS = PORTD.4#option LCD_EN = PORTD.5

Include "hs_lcd.bas"Include "convert.bas"

Dim encoder_last_a As BitDim encoder_last_b As BitDim encoder_now_a As BitDim encoder_now_b As BitDim button_l As PORTE.0 // Rotary encoder LeftDim button_r As PORTE.1 // Rotary encoder RightDim index As Word // variable to hold duty cycle valueindex = 0

ClsWriteAt(1,1, "index = ", DecToStr(index), " ")CCP2CON = %00001100TRISB.3 = 0 // set B3 as output to drive LED4T2CON = %00000100

Page 19: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 19 of 88

While (TRUE) encoder_now_a = button_l encoder_now_b = button_r If encoder_now_a <> encoder_last_a Or encoder_now_b <> encoder_last_b Then If encoder_last_a=1 And encoder_last_b=1 And encoder_now_a=0 And encoder_now_b=1 Then If (index <= 1022) Then Inc(index) WriteAt(1,9, DecToStr(index), " ") EndIf EndIf If encoder_last_a=0 And encoder_last_b=1 And encoder_now_a=1 And encoder_now_b=1 Then If (index >= 1) Then Dec(index) WriteAt(1,9, DecToStr(index), " ") EndIf EndIf EndIf encoder_last_a = encoder_now_a encoder_last_b = encoder_now_b CCP2CON.5 = index.1 // load duty cycle LSBs CCP2CON.4 = index.0 CCPR2L = index >>2 // load duty cycle MSBs Wend

Page 20: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 20 of 88

{DutyCycle8 HamStack with 18F4620; DEV-1 board}Device = 18F4620 Clock = 10 #option LCD_DATA = PORTD.0#option LCD_RS = PORTD.4#option LCD_EN = PORTD.5

Include "hs_lcd.bas"Include "convert.bas"

Dim encoder_last_a As BitDim encoder_last_b As BitDim encoder_now_a As BitDim encoder_now_b As BitDim button_l As PORTE.0 ' Rotary encoder LeftDim button_r As PORTE.1 ' Rotary encoder RightDim index As Word

Function DutyCycle(pValue As Word) As Word result = (pValue*10000)/1024End Function

index = 0ClsWriteAt(1,1, "index = ", DecToStr(index), " ")WriteAt(2,1, " duty = ", DecToStr(DutyCycle(index)/100),".",DecToStr(DutyCycle(index),2),"%")CCP2CON = %00001100TRISC.1 = 0T2CON = %00000100

In the following program, each turn of the encoder adds or subtracts 16 from the index variable so that slightly more than 3 full encoder turns will get to the full scale of 1023.

The approximate duty cycle is calculated and displayed using the same LCD display idea in the Analog Input program in the HamStack Microcontroller Project Platform manual.

The DEV-1 board is used with its encoder, LCD and LED4.

Page 21: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 21 of 88

While (TRUE) encoder_now_a = button_l encoder_now_b = button_r

If encoder_now_a <> encoder_last_a Or encoder_now_b <> encoder_last_b Then If encoder_last_a=1 And encoder_last_b=1 And encoder_now_a=0 And encoder_now_b=1 Then If (index <= 992) Then index = index + 16 ElseIf (index = 1008) Then index = 1023 EndIf

WriteAt(1,9, DecToStr(index)) WriteAt(2,9, DecToStr(DutyCycle(index)/100),".",DecToStr(DutyCycle(index),2),"% ") EndIf

If encoder_last_a=0 And encoder_last_b=1 And encoder_now_a=1 And encoder_now_b=1 Then If (index = 1023) Then index = 1008 ElseIf (index >= 16) Then index = index - 16 EndIf WriteAt(1,9, DecToStr(index), " ") WriteAt(2,9, DecToStr(DutyCycle(index)/100),".",DecToStr(DutyCycle(index),2),"% ") EndIf

EndIf

encoder_last_a = encoder_now_a encoder_last_b = encoder_now_b CCP2CON.5 = index.1 CCP2CON.4 = index.0 CCPR2L = index >>2 Wend

Page 22: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 22 of 88

The code below has been modified to work with the 18F46K22 on the DEV-1 board.

Each turn of the encoder adds or subtracts 8 from the index variable so that slightly more than 6 full encoder turns will get to the full scale of 1023.

The one code line in red allows the appropriate SFRs to be set with the SE version of Swordfish. Both the hs_convert.bas and the hs_lcd.bas libraries call the hs_utils.bas library which has the necessary macro and statements to make the PORTD pins digital.

{DutyCycle9 HamStack with 18F46K22; DEV-1 board}Device = 18F46K22 Clock = 16 Config PLLCFG = OFF, CCP2MX = portB3

#option SWORDFISH_SE = true // SFR Fix #option LCD_DATA = PORTD.0#option LCD_RS = PORTD.4#option LCD_EN = PORTD.5Include "hs_convert.bas"Include "hs_lcd.bas"

Dim encoder_last_a As BitDim encoder_last_b As BitDim encoder_now_a As BitDim encoder_now_b As BitDim button_l As PORTE.0 ' Rotary encoder LeftDim button_r As PORTE.1 ' Rotary encoder RightDim index As Word

Function DutyCycle(pValue As Word) As Word result = (pValue*10000)/1024End Function

index = 0ClsWriteAt(1,1, "index = ", DecToStr(index), " ")WriteAt(2,1, " duty = ", DecToStr(DutyCycle(index)/100),".",DecToStr(DutyCycle(index),2),"%")CCP2CON = %00001100TRISB.3 = 0T2CON = %00000100

Page 23: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 23 of 88

While (TRUE) encoder_now_a = button_l encoder_now_b = button_r If encoder_now_a <> encoder_last_a Or encoder_now_b <> encoder_last_b Then If encoder_last_a=1 And encoder_last_b=1 And encoder_now_a=0 And encoder_now_b=1 Then If (index <= 1008) Then index = index + 8 ElseIf (index = 1016) Then index = 1023 EndIf WriteAt(1,9, DecToStr(index)) WriteAt(2,9, DecToStr(DutyCycle(index)/100),".",DecToStr(DutyCycle(index),2),"% ") EndIf If encoder_last_a=0 And encoder_last_b=1 And encoder_now_a=1 And encoder_now_b=1 Then If (index = 1023) Then index = 1016 ElseIf (index >= 8) Then index = index - 8 EndIf WriteAt(1,9, DecToStr(index), " ") WriteAt(2,9, DecToStr(DutyCycle(index)/100),".",DecToStr(DutyCycle(index),2),"% ") EndIf EndIf encoder_last_a = encoder_now_a encoder_last_b = encoder_now_b CCP2CON.5 = index.1 CCP2CON.4 = index.0 CCPR2L = index >>2 Wend

Page 24: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 24 of 88

Here’s how the duty cycle was calculated, using the variable index:

DC% = Pulse Width(100) = (CCPR2L:CCP2CON<5:4>)(Tosc)(TMR2 Prescale)(100) PWM Period (PR2 + 1}(4)(Tosc)(TMR2 Prescale)

= index(100) = index(100) (256)(4) 1024

Example: index = 80 DC = 80(100) = 7.81% 1024

Here’s how the duty cycle was displayed on the LCD:

The value of index is sent into the DutyCycle function: 80(10000) / 1024 = 781

DecToStr(DutyCycle(index) / 100 ---> DecToStr(781/100) = 7 --> prints 7

Then print a decimal point with ,”.”,

DecToStr(DutyCycle(index), 2) --> DecToStr(781) = 781 --> prints 81

Then print a percent sign with ,”%”)

Final result = 7.81%

Page 25: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 25 of 88

Period

As can be seen from the equation above (46K22, DS pg. 187), the PWM Period is determined by 3 factors:

• The value in the timer’s Period Register, PRx• The chip’s oscillator frequency, Tosc• The value of the prescale bits in the timer’s control register TMRx, determined

by the bits in the TxCON register.

{Period1: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF Dim index As Byte, UpDir As Booleanindex = 255

CCP2CON = %00001100 // enable PWM on CCP2TRISC.1 = 0 // pin 16 18F46K22; scope this pinT2CON = %00000100 // start Timer2index = 255

While (TRUE) If (index = 255) Then UpDir = false ElseIf (index = 3) Then UpDir = true EndIf

If (UpDir) Then index = index + 4 Else index = index - 4 EndIf PR2 = index CCPR2L = (index/2)+1 DelayMS(50)Wend

Page 26: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 26 of 88

The above code, Period1, keeps a 50% duty cycle through 64 different periods, from 64 µs (15625 Hz) to 1 µs (1 MHz), by decreasing and increasing the value in PR2. The value in CCPR2L is scaled so as to keep the 50% duty cycle.

The waveforms can be more easily seen by roughly increasing the delay as the period gets shorter by changing the DelayMS(50) to: DelayMS(255 - index) + 200).

Or the 5 shortest periods can be slowed way down for display with code something like this: If (index <= 19) then DelayMS(5000) else DelayMS(255 - index) EndIf

This code works as well on the 18F4620 by changing:

Device = 18F46K22 TO Device = 18F4620 Clock = 16 Clock = 10 Config PLLCFG = OFF // Config PLLCFG = OFF

The waveform can be listened to on the DEV-1 speaker by changing:

CCP2CON TO CCP1CON TRISC.1 TRISC.2 CCPR2L CCPR1L AND removing any Delay(MS) statements and adding:

If(UpDir) Then DelayMS(2) Else DelayMS(3) EndIf

Example Duty Cycle Ratio calculation when index = 19

CCPR2L = (19/2) + 1 = 10 = %00001010 CCPxCON<5:4> = %00 CCPRxL:CCPxCON<5:4> = %0000101000 = 40

PR2 = index = 19; 4(19 + 1) = 80

Duty Cycle Ratio = 40 / 80 = .5 = 50%

Page 27: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 27 of 88

The period depends on the oscillator frequency. The code below clocks the chip at 64 MHz to deliver a 64 µs period (PR2 = power-up default of 255)

{Period2: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16

CCP2CON = %00001100 // enable PWM on CCP2TRISC.1 = 0 // pin 16 18F46K22; scope this pinCCPR2L = 128 // load for 50% duty cycleT2CON = %00000100 // start Timer 2

While (TRUE)Wend

Changing the code as shown below generates a short period but one still an order of magnitude bigger than the shortest possible.

PR2 = 2 // load for ̃ 188 ns periodCCPR2L = 1 // load for 33% duty cycle

The 18F46K22 powers up with slew rate control (46K22 DS pg. 153, 158)

Page 28: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 28 of 88

Adding the line below will restore maximum slew rate. Pictures below are from a Tek 2215 60 MHz scope, .05 µs / div.

SLRCON = %00011011 // remove slew rate limitation on PORTC

with slew rate limitation! ! ! ! without slew rate limitation

Page 29: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 29 of 88

The code below should generate the shortest period and the lowest duty cycle, running the HamStack with 18F46K22 at 64 MHz, minimum PR2, and no oscillator prescaling.

{Period3: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 64

SLRCON = %00011011 // remove PORTC slew rate limitingCCP2CON = %00011100 // enable PWM on CCP2 and one LSBTRISC.1 = 0 // pin 16 18F46K22; scope this pinPR2 = 0 // load for shortest period ̃ 63 nsCCPR2L = 0 // load for shortest duty cycleT2CON = %00000100 // start Timer 2

While (TRUE)Wend

The third way to adjust the period is by setting the prescaler bits (46K22 DS pg. 177). Using the code above and changing a single bit will increase the period sixteen times to 1 µs (16)(62.5 ns).

T2CON = %00000110 // start Timer 2; prescaler = 16

50 ns / div

500 ns / div

Page 30: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 30 of 88

{Period4: HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Config OSC = HSPLL

CCP2CON = %00011100 // enable PWM on CCP2 and one LSBTRISC.1 = 0 // pin 16 18F4620; scope this pinPR2 = 0 // load for shortest period ̃ 100 nsCCPR2L = 0 // load for shortest duty cycleT2CON = %00000100 // start Timer 2

While (TRUE)Wend

The code above generates the shortest period, 100 ns, using the 18F4620 clocked at its maximum of 40 MHz. As before, changing one bit to prescale at 16 would increase the period to ~ 1.6 µs.

Generating the longest period from either chip would mean setting the prescaler to 16, leaving the Period Register PRx at its maximum of 255, and running a slow oscillator. The chip could be clocked externally at an extremely slow rate, even down to DC (46K22 DS pg. 449).

The code below uses the HamStack with 18F4620 in its normal configuration running at 10 MHz but with full prescaling of 16.

The period should be, and is when measured, (256)(4)(0.0000001)(16) = 0.0016384 or ~ 1.6 ms

{Period5: HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10

CCP2CON = %00001100 // enable PWMTRISC.1 = 0 // pin 16 18F4620; scope this pinCCPR2L = 128 // load for 50% duty cycleT2CON = %00000110 // start Timer 2; prescaler = 16

While (TRUE)Wend

Page 31: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 31 of 88

{Period6: HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10

OSCCON = %00000010 // use internal oscillator at 31 kHzCCP2CON = %00001100 // enable PWMTRISC.1 = 0 // pin 16 18F4620; scope this pinCCPR2L = 128 // load for 50% duty cycleT2CON = %00000110 // start Timer2; prescaler = 16

While (TRUE)Wend

The code below adds a single line, setting the OSCCON register to use its slowest internal oscillator INTRC at approximately 31 kHz (4620 DS pg. 32).

The period would then be: (256)(4)(0.00003225)(16) = 0.528 or ~ one half second.

4620 DS pg. 382 shows that INTRC can vary by approximately 10 kHz across temperature.

A Saleae Logic probe measures the period as .503 seconds. Assuming the Logic probe is fairly accurate capturing this low frequency signal, we can calculate the approximate frequency of INTRC.

.503 / (1024)(16) = ~0.0000307 = ~32572 Hz

Through only code changes, the 18F4620 can generate periods from .5 second to 100 ns, or more than six orders of magnitude.

Page 32: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 32 of 88

The code below uses the HamStack with 18F46K22 in its normal configuration running at 16 MHz but with full prescaling of 16.

The period should be, and is when measured, (256)(4)(0.0000000625)(16) = 0.001024 or ~ 1.0 ms

{Period7: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF

CCP2CON = %00001100 // enable PWMTRISC.1 = 0 // pin 16 18F46K22; scope this pinCCPR2L = 128 // load for 50% duty cycleT2CON = %00000110 // start Timer 2; prescaler = 16

While (TRUE)Wend

The code below adds a single line, setting the OSCCON register to use its slowest internal oscillator INTRC at approximately 31.25 kHz (46K22 DS pg. 32).

The period would then be: (256)(4)(0.000032)(16) = 0.524 or ~ one half second.

{Period8: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF

OSCCON = %00000010 // use internal oscillator at 31.25 kHz CCP2CON = %00001100 // enable PWMTRISC.1 = 0 // pin 16 18F46K22; scope this pinCCPR2L = 128 // load for 50% duty cycleT2CON = %00000110 // start Timer 2; prescaler = 16

While (TRUE)Wend

Page 33: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 33 of 88

A Saleae Logic probe measured the period as .533 seconds. Assuming the Logic probe is fairly accurate capturing this low frequency signal, we can calculate the approximate frequency of INTRC.

.533 / (1024)(16) = ~0.0000325 = ~30769 Hz

Through only code changes, the 18F46K22 can generate periods from .5 second to 63 ns, or more than six orders of magnitude.

Page 34: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 34 of 88

Multiple CCPs

{MultipleCCPs1: HamStack with 18F4620; DEV-! board optional}Device = 18F4620Clock = 10

CCP1CON = %00001100CCP2CON = %00001100

TRISC.2 = 0TRISC.1 = 0

CCPR1L = 212CCPR2L = 48

T2CON = %00000100

While (true)Wend

The 18F4620 has two CCPs both of which use Timer2. This chip can therefore generate, using both CCPs simultaneously, 1 period and 2 duty cycles.

With the 10 MHz crystal as the oscillator, the default power-up PR2 value of 255, and no pre-scaling, the code below generates PWM on both C1 and C2 with a period of 102.4 µs.

Pin C2, controlled by CCP1, has a duty cycle of 82.8%. Pin C1, controlled by CCP2, has a duty cycle of 18.75%

These Saleae Logic probe results agree fairly well with the calculated values.

Page 35: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 35 of 88

The 18F46K22 has five CCPs, any of which can use 3 different Timers for PWM, Timer2, Timer4, or Timer6.

Consequently this chip can simultaneously generate PWM with 3 different periods and 5 different duty cycles.

To program the 18F46K22 for 5 duty cycles and 3 periods, the choices on the left shown below were made. The values on the right half were calculated from those choices on the left.

The decimal value representing the ten bit value CCPRxL:CCPxCON<5:4> was rounded before being converted to CCPRxL and the two LSBs.

Calculation Examples:

PR4 and Pre(scale) value:Given a Period of 128 µs, (PR4 + 1)(prescale value) = .000128 / (4)(0.0000000625) = 512

Since PR4 cannot be larger than 255: (PR4 + 1) = 512 / (try 4 for prescale value) = 128 PR4 = 128 - 1 = 127 and Pre(scale) = 4

10 bit decimal value for duty cycle:Given a duty cycle ratio of .1, CCPRxL:CCPxCON<5:4> = (.1)(4)(PR4 + 1) = 51.2 round(51.2) = 51 51 = % 00 0011 0011 = 0000 1100 11 (separating out the two LSBs) CCPR4L = %00001100 = 12 LSBs = %11 = 3

Page 36: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 36 of 88

{MultipleCCPs2: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP3MX = PORTE0

#option SWORDFISH_SE = true // SFR FixPublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro

write_sfr(CCPTMRS1, %00001001) // CCP5 = Timer6; CCP4 = Timer4CCP1CON = %00001100 // LSBs = 0CCP2CON = %00001100 // LSBs = 0write_sfr(CCP3CON, %00001100) // LSBs = 0write_sfr(CCP4CON, %00111100) // LSBs = 3write_sfr(CCP5CON, %00111100) // LSBs = 3

CCPR1L = 192CCPR2L = 128write_sfr(CCPR3L, 64)write_sfr(CCPR4L, 12)write_sfr(CCPR5L, 12)

TRISC.1 = 0TRISC.2 = 0TRISE.0 = 0TRISD.1 = 0TRISE.2 = 0

PR2 = 255write_sfr(PR4, 127)write_sfr(PR6, 255)

T2CON = %00000100 // prescale = 1write_sfr(T4CON, %00000101) // prescale = 4write_sfr(T6CON, %00000101) // prescale = 4

While (TRUE)Wend

Page 37: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 37 of 88

In the Saleae Logic probe screenshot below, there are 3 different periods and 5 different duty cycles.

Calculated Results Measured Results

Period: 64 µsDuty Cycle: 75 %

Period: 64 µsDuty Cycle: 50 %

Period: 64 µsDuty Cycle: 25 %

Period: 128 µsDuty Cycle: 10 %

Period: 256 µsDuty Cycle: 5 %

Page 38: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 38 of 88

Those values for the period and duty cycle chosen in the previous example were all “sort of easy”.

For example, the default PWM period of the 18F46K22 running at 16 MHz with the default power-up PR2 value of 255 would be 64 µs.

To recheck this whole exercise more, hopefully, odd values were chosen as shown below.

Calculations were done as before to get the required values on the righthand side.

Page 39: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 39 of 88

{MultipleCCPs3: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP3MX = PORTE0

#option SWORDFISH_SE = true // SFR FixPublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro

write_sfr(CCPTMRS1, %00001001) // CCP5 = Timer6; CCP4 = Timer4CCP1CON = %00111100 // LSBs = 3 CCP2CON = %00101100 // LSBs = 2write_sfr(CCP3CON, %00101100) // LSBs = 2write_sfr(CCP4CON, %00111100) // LSBs = 3write_sfr(CCP5CON, %00101100) // LSBs = 2

CCPR1L = 57CCPR2L = 36write_sfr(CCPR3L, 14)write_sfr(CCPR4L, 25)write_sfr(CCPR5L, 3)

TRISC.1 = 0TRISC.2 = 0TRISE.0 = 0TRISD.1 = 0TRISE.2 = 0

PR2 = 84write_sfr(PR4, 111)write_sfr(PR6, 85)

T2CON = %00000101 // prescale = 4write_sfr(T4CON, %00000101) // prescale = 4write_sfr(T6CON, %00000110) // prescale = 16

While (TRUE)Wend

Page 40: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 40 of 88

In the Saleae Logic probe screenshot below, there again are 3 different periods and 5 different duty cycles.

Calculated Results Measured Results

Period: 85 µsDuty Cycle: 68 %

Period: 85 µsDuty Cycle: 43 %

Period: 85 µsDuty Cycle: 17 %

Period: 112 µsDuty Cycle: 23 %

Period: 339 µsDuty Cycle: 4 %

Page 41: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 41 of 88

The 18F46K22 oscillator can be changed from the 16 MHz crystal to its internal, factory calibrated, 16 MHz oscillator by adding one line of code:

OSCCON = %01110010

The internal oscillator can then be tuned above and below its calibrated value by adding one additional line of code:

OSCTUNE = %00100000 // tune to minimum frequency OR OSCTUNE = %00000000 // factory calibrated frequency OR OSCTUNE = %00011111 // tune to maximum frequency

The Saleae Logic probe measurements change slightly as you move the cursor along the captured waveforms.

Below are both the minimum and maximum Saleae measurements for the 85 µs period with the internal oscillator set for minimum, factory, and maximum. Delta Hz shows the Saleae uncertainty of approximately 6 Hz capturing the 85 µs period while running at its maximum acquisition speed.

The frequency of the internal 16 MHz oscillator appears to be slightly higher and differ from the crystal oscillator by less than 1 %.

OSCTUNE Period Minimum Hz Maximum Hz Delta Hz

%00100000 1 11,478 11,483 5

%00000000 1 11,846 11,852 6

%00011111 1 12,158 12,164 6

crystal 1 11,759 11,765 6

Page 42: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 42 of 88

Enhanced CCPs

{EnhancedCCP1: HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10 Dim index As Byte, UpDir As Booleanindex = 255

CCP1CON = %10001100 // enable Half-Bridge on ECCP1TRISC.2 = 0 // make P1A an output, i.e. pin C2TRISD.5 = 0 // make P1B an output, i.e. pin D5T2CON = $00000100

While (TRUE) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf

If (UpDir) Then Inc(index) Else Dec(index) EndIf CCPR1L = index DelayMS(4)Wend

The 18F4620 has 2 CCP (Capture/Compare/PWM) modules. Although CCP1 can operate in standard PWM mode, it is also an Enhanced CCP or ECCP.

The enhanced mode can simultaneously drive two pins (Dual PWM: Half-Bridge; P1A and P1B) or four pins (Quad PWM: Full Bridge; P1A, P1B, P1C, and P1D).

The code above drives 2 pins with complementary output levels. LEDs hooked up to both output pins will brighten and dim out of phase.

Page 43: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 43 of 88

{EnhancedCCP2: HamStack with 18F4620; DEV-1 board optional}Device = 18F4620Clock = 10 Dim index As Byte, UpDir As Booleanindex = 255

CCP1CON = %10001100 // enable Half-Bridge on Enhanced CCP1PWM1CON = %01111111 // set for maximum dead-band delayTRISC.2 = 0 // make P1A an output, i.e. C2TRISD.5 = 0 // make P1B an output, i.e. D5T2CON = %00000100

While (TRUE) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf

If (UpDir) Then Inc(index) Else Dec(index) EndIf CCPR1L = index DelayMS(4)Wend

If the complementary outputs are driving a real world load, then there could be “shoot-through current.

This can be avoided by programming one additional register for dead-band delay, so both outputs are never simultaneously on or off at the same time.

(4620 DS pg. 158-160)

Page 44: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 44 of 88

The 18F46K22 has 5 CCP (Capture/Compare/PWM) modules. Although CCP1-3 can operate in standard PWM mode, they also can operate as Enhanced CCPs or ECCPs.

1 ECCP can simultaneously drive two pins (Dual PWM: Half-Bridge) and 2 ECCPs can drive two pins or four pins (Quad PWM: Full Bridge).

ECCP OUTPUT pins

18F4620: ECCP1 Half Bridge P1A P1B C2 D5

Full Bridge P2A P2B P2C P2D C2 D5 D6 D7

18F46K22 ECCP1 Full or Half Bridge P1A P1B P1C P1D C2 D5 D6 D7

ECCP2 Full or Half Bridge P2A P2B P2C P2D (erased chip default pins) C1 D2 D3 D4 (set by config statement) B3 (CCP2MX fuse) C0 (P2BMX fuse)

ECCP3 Half Bridge P3A P3B (erased chip default pins) B5 E1 set by config statement) E0 (CCP3MX fuse)

18F46K22 Config statements (appropriate TRIS statements also needed) Config CCP2MX = PORTC1 Pin C1 is output; default if chip is erased or Config CCP2MX = PORTB3 Pin B3 is output

Config CCP3MX = PORTB5 Pin B5 is output; default if chip is erased or (B5 is grounded on HamStack board) Config CCP3MX = PORTE0 Pin E0 is output

Config P2BMX = PORTD2 Pin D2 is output; default if chip is erased or Config P2BMX = PORTC0 Pin C0 is output

Page 45: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 45 of 88

{EnhancedCCP3: HamStack with 18F46K22; DEV-1 board}

Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP2MX = PORTB3, P2BMX = PORTC0

Dim index As Byte, UpDir As Booleanindex = 255

CCP2CON = %10001100 // enable enhanced mode half-bridgePWM2CON = %01111111 // maximum dead-band delayTRISB.3 = 0TRISC.0 = 0T2CON = %00000100

While(true) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf If (UpDir) Then Inc(index) Else Dec(index) EndIf CCPR2L = index DelayMS(4)Wend

Both 18F46K22 ECCP modules which can drive full-bridge can also drive half-bridge (ECCP1 and ECCP2)

The code below uses both the status LED and LED4 on the DEV-1 board as well as the default Timer2 and default power-up value of 255 in PR2.

Once the code is running, comment out {PLLCFG = OFF} to let the board run at 64 MHz; you can still see on a scope that both LEDs are being pulse width modulated.

Page 46: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 46 of 88

Duplicating the previous result using the Half-Bridge module, ECCP3, is more difficult on the DEV-1 board:

uses different output pins (default P3A = B5, P3B = E1) but configure P3A to E0 (B5 is grounded on the HamStack board) remove Enc_A and Enc_B jumpers on DEV-1 use SFR fix.

{EnhancedCCP4: HamStack with 18F46K22; DEV-1 board}Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP3MX = PORTE0 // set P3A = E0

#option SWORDFISH_SE = true // SFR fixPublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro Dim index As Byte, UpDir As Booleanindex = 255

write_sfr(CCP3CON, %10001100) // enable ECCP3 Half-BridgeTRISE.0 = 0 // make P3A (pin E0) an outputTRISE.1 = 0 // make P3B (pin E1) an outputT2CON = %00000100

While (TRUE) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf

If (UpDir) Then Inc(index) Else Dec(index) EndIf write_sfr(CCPR3L, index) DelayMS(4)Wend

Page 47: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 47 of 88

The following code shows some features of Full-Bridge PWM with direction change on the DEV-1 board using the 18F46K22.

IO ports B3 (DEV-1 LED4), C0 (HamStack status LED), and D4 are used; current limited LEDs have been added to pins D3 and D4.

Full-Bridge output reverse OFF: DEV-1 LED4( B3); D4 LED D3 LED turns on at full brightness status LED C0 turns on and ramps up to full brightness.

Full-Bridge output forward OFF: status LED (C0); D3 LED LED4 turns on at full brightness D4 LED comes on at full brightness and ramps off.

When the variable index is being incremented, CCP2CON is set for Full-Bridge output reverse (%11001100). This condition causes P2B (C0 status LED) to be modulated and P2C (D3) to be active (5V), while P2A(B3) and P2D (D4) are inactive (GND).

When the variable index starts to be decremented, there is a direction change to Full-Bridge output forward (CCP2CON = %01001100).

This condition causes P2D (D4 LED) to be modulated and P2A (B3 = DEV-1 LED4) to be active (5V). P2B (CO or status LED) and P2C (D3 LED) are inactive (GND).

46K22 DS pg. 206

All these conditions are easier to observe at a 4 MHz clock speed by uncommenting

//osccon = %01010010

This code line causes the HamStack to use its internal oscillator at 4 MHz rather than the 16 MHz crystal.

Like many previous programs, this one relies on Timer2 being the default timer for the CCP and ECCP modules and that its Period Register PR2 comes loaded with 255 on power-up.

The same results can be seen using PWM1CON, CCP1CON, CCPR1L, making C2, D5, D6, and D7 outputs with TRIS statements, and putting LEDs on those 4 outputs.

Page 48: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 48 of 88

{EnhancedCCP5: HamStack with 18F46K22; DEV-1 board}Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP2MX = PORTB3, P2BMX = PORTC0

Dim index As Byte, UpDir As Booleanindex = 255

PWM2CON = %01111111 // maximum dead-band delayTRISB.3 = 0 // LED4 on DEV-1 boardTRISC.0 = 0 // status LED on HamStack boardTRISD.3 = 0 // add current limited LED to D3TRISD.4 = 0 // add current limited LED to D4T2CON = %00000100//osccon = %01010010

While(true) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf If (UpDir) Then Inc(index) CCP2CON = %11001100 // set Full-Bridge output reverse Else Dec(index) CCP2CON = %01001100 // set Full_Bridge output forward EndIf CCPR2L = index DelayMS(4) Wend

Page 49: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 49 of 88

Steering

The 18F46K22, unlike the 18F4620, has a PWM Steering Mode. where the PWM signal can be put on any or all of the four output pins, PxA - PxD (46K22 DS pg. 200).

The code below identically modulates 4 pins at the same time:

B3 (Dev-1 LED4) C0 (HamStack status LED) D3 (LED added) D4 (LED added)

{Steering1: HamStack with 18F46K22; DEV-1 board}

Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP2MX = PORTB3, P2BMX = PORTC0

Dim index As Byte, UpDir As Booleanindex = 255

CCP2CON = %00001100 // put ECCP2 in single output PWM modePSTR2CON = %00001111 // steer PWM to all 4 output pinsTRISB.3 = 0TRISC.0 = 0TRISD.3 = 0TRISD.4 = 0T2CON = %00000100

While(true) If (index = 255) Then UpDir = false ElseIf (index = 0) Then UpDir = true EndIf If (UpDir) Then Inc(index) Else Dec(index) EndIf CCPR2L = index DelayMS(4) Wend

Page 50: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 50 of 88

Sleep & Idle

{Sleep1: HamStack with 18F4620; DEV-1 board}Device = 18F4620Clock = 10Config CCP2MX = PORTBE, WDTPS = 512 // WDT OFF from Include file

#option WDT = true // compiler tickles WDT in delay routinesCCP2CON = %00001100TRISB.3 = 0 // shows on LED4 on DEV-1 boardT2CON = %00000100WDTCON = %00000001 // turn on WDT via software

While (TRUE)CCPR2L = 192 // 75% duty cycleDelayMS(2048)

Asm sleep // had to have WDT on to wake chip from sleepEnd Asm

CCPR2L = 16 // 6.25% duty cycleDelayMS(2048)

Asm sleepEnd Asm

Wend

After 2+ seconds, the code above puts the chip to sleep, which shuts off all clock sources, thus no longer clocking the CCP module, set for, at first, a 75% duty cycle.

When awakened by the Watch Dog Timer (WDT), the code executes in place, thereby clocking the CCP with a 6.25% duty cycle, sleeps, and back to 75%.

The WDT times out after the chip goes to sleep in approximately 2+ seconds. When the WDT is enabled, an internal oscillator is turned on ( ~32 kHz). The 32k clock is divided by 128 = 250 Hz or 4 ms. The WDTPS (Watch Dog Timer Post Scaler) multiples 4 ms by 512 to get 2.048 seconds.

The CCP2 output was redirected from RC1 to RB3 to show on LED4 on the DEV-1 board. The output is left either high or low when the chip goes to sleep as no effort was made to leave the shutdown CCP in a known state.

Page 51: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 51 of 88

{Sleep2: HamStack with 18F4620; DEV-1 board}Device = 18F4620Clock = 10Config CCP2MX = PORTBE, WDTPS = 512

#option WDT = true // compiler tickles WDT in delay routinesCCP2CON = %00001100TRISB.3 = 0 // B3, pin 38T2CON = %00000100OSCCON = %11000000 // enable Idle bitWDTCON = %00000001

While (TRUE)CCPR2L = 192 // 75% duty cycleDelayMS(2048)

Asm sleepEnd Asm

CCPR2L = 16 // 6.25% duty cycleDelayMS(2048)

Asm sleepEnd Asm

Wend

After 2+ seconds, the code above puts the chip to sleep, but unlike before, the Idle bit has been set so that the primary oscillator keeps running, thus clocking the CCP module. On a scope the PWM does not disappear like before.

Each PWM duty cycle runs for about 4 seconds (2 from the delay + 2 from the WDT).

Page 52: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 52 of 88

{Sleep3: HamStack with 18F46K22; DEV-1 board}Device = 18F46K22Clock = 16Config PLLCFG = OFF, CCP2MX = PORTB3, WDTPS = 512

#option WDT = true // compiler tickles WDT in delay routinesCCP2CON = %00001100TRISB.3 = 0 // shows on LED4 on DEV-1 boardT2CON = %00000100WDTCON = %00000001 // turn on WDT via software

While (TRUE)CCPR2L = 192 // 75% duty cycleDelayMS(2048)

Asm sleep // had to have WDT on to wake chip from sleepEnd Asm

CCPR2L = 16 // 6.25% duty cycleDelayMS(2048)

Asm sleepEnd Asm

Wend

The Sierra 18F46K22.bas Include file sets the WDT to be controlled by software and sets the Postscaler to 8192. So the Postscaler is decreased in the Config statement to 512 which is approximately 2.048 seconds and the WDTCON is set to turn the WDT on.

As before, without the Idle bit set, entering sleep will turn the primary oscillator off so CCP2 will not be clocked for a pattern of 2+ seconds high duty cycle, 2+ seconds off, and 2+ seconds low duty cycle.

Enabling the Idle bit as in Sleep2 will keep clocking CCP2 giving the pattern: 4+ seconds high duty cycle, 4+ seconds low duty cycle.

Page 53: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 53 of 88

Servo

The Parallax Continuous Rotation Servo rotates CCW with a 1.7 ms pulse, CW with a 1.3 ms pulse, and holds with a 1.5 ms pulse.

“In order for smooth rotation, the servo needs a 20 ms pause between pulses” (#900-00008 data sheet pg. 5).

Parallax generates the required PWM by generating a pulse and then separately generating the 20 ms delay.

This experiment will approximate the required conditions by setting the HamStack for PWM, to generate both the pulse and the delay together. The minimum and maximum periods are calculated below for all oscillator frequencies.

Page 54: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 54 of 88

pulse ms

pulse + 20 ms

servo action

1.3 21.3 max CW rotation

1.5 21.5 holding

1.7 21.7 max CCW rotation

Needing a period of approximately 21-22 ms, oscillator frequencies will have to be 500 kHz or lower with prescale set to 16.

Calculating PR2 + 1: 21.3 ms: PR2 + 1 = .0213 / (4)(.000002)(16) = 166.4 21.5 ms: PR2 + 1 = .0215 / (4)(.000002)(16) = 167.9 21.7 ms: PR2 + 1 = .0217 / (4)(.000002)(16) = 169.5

Calculating the duty cycle ratios: 21.3 ms: 1.3 / 21.3 = 0.061032 21.5 ms: 1.5 / 21.5 = 0.069767 21.7 ms: 1.7 / 21.7 = 0.078341

Calculating the duty cycle ten bits: 21.3 ms: (CCPR2L:CCP2CON<5:4>) = (0.061032)(4)(166.4) = 40.6 21.5 ms (CCPR2L:CCP2CON<5:4>) = (0.069767)(4)(167.9) = 46.9 21.7 ms: (CCPR2L:CCP2CON<5:4>) = (0.078341)(4)(169.5) = 53.1

The values for PR2 and index shown below should produce PWM close to the intended values.

Only these two values need to be changed in code to achieve all three states.

servo action pulse ms pulse + 20 ms PR2 index

max CW rotation 1.3 21.3 166 41

holding 1.5 21.5 167 47

max CCW rotation 1.7 21.7 169 53

Page 55: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 55 of 88

{Servo1: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Config PLLCFG = OFF Dim index As Byteindex = 47

OSCCON = %00100010 // internal 500 kHz oscillatorCCP2CON = %00001100 // enable PWM on CCP2PR2 = 167 // calculated valueTRISC.1 = 0 // pin 16 18F46K22; servo controlT2CON = %00000110 // start Timer2; prescale = 16CCP2CON.5 = index.1 // load duty cycle LSB 1CCP2CON.4 = index.0 // load duty cycle LSB 0CCPR2L = index >> 2 // load duty cycle MSBs

While (TRUE)Wend

That worked! The servo pot was adjusted for no rotation.

The servo twitches slightly CW once every 15 - 20 HamStack resets, and also when the power is cycled.

Below are Saleae Logic probe screenshots, in close agreement with the calculated values

The code below, using an internal 500 kHz oscillator, prescale = 16, PR2 = 167 and duty cycle = 47, should produce an approximate 1.5 ms pulse followed by 20 ms.

This output will be used to center the servo, by adjusting the pot on the servo until rotation stops.

Page 56: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 56 of 88

index = 53PR2 = 169 // CCW rotation

index = 41PR2 = 166 // CW rotation

index = 47PR2 = 167 // servo pot adjusted to hold

Page 57: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 57 of 88

The following code combines all three states using the DEV-1 board. On power-up the servo has been adjusted for no rotation.

Turning the encoder once CCW will cause maximum servo CCW rotation. Turning back CW will stop it, and one more turn CW will cause maximum servo CW rotation.

{servo2 HamStack with 18F46K22; DEV-1 board}Device = 18F46K22Config PLLCFG = OFF

#option SWORDFISH_SE = true // SFR fixPublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro

Dim encoder_last_a As BitDim encoder_last_b As BitDim encoder_now_a As BitDim encoder_now_b As BitDim button_l As PORTE.0 ' Rotary encoder LeftDim button_r As PORTE.1 ' Rotary encoder RightDim index As ByteDim indexcopy As Byte

index = 47 // startup duty cycle for no rotationwrite_sfr(ANSELE, %11111100) // turn analog input off on E0 & E1OSCCON = %00100010 // internal 500 kHz oscillatorCCP2CON = %00001100PR2 = 167 // startup periodCCP2CON.4 = index.0 // load duty cycle LSBsCCP2CON.5 = index.1indexcopy = indexCCPR2L = indexcopy >>2 // load duty cycle MSBsT2CON = %00000110 // enable Timer2, prescale = 16TRISC.1 = 0

Page 58: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 58 of 88

While (TRUE)encoder_now_a = button_lencoder_now_b = button_rIf encoder_now_a <> encoder_last_a Or encoder_now_b <>encoder_last_b Then If encoder_last_a=1 And encoder_last_b=1 And encoder_now_a=0 And encoder_now_b=1 Then If (index = 53) Then index = 47 PR2 = 167 ElseIf (index = 47) Then index = 41 PR2 = 166 EndIf EndIf If encoder_last_a=0 And encoder_last_b=1 And encoder_now_a=1 And encoder_now_b=1 Then If (index = 41) Then index = 47 PR2 = 167 ElseIf (index = 47) Then index = 53 PR2 = 169 EndIf EndIfEndIfencoder_last_a = encoder_now_aencoder_last_b = encoder_now_bIf (indexcopy <> index) Then // load new PR2 and duty cycle indexcopy = index // values only if they have CCPR2L = indexcopy >>2 // changed CCP2CON.4 = index.0 CCP2CON.5 = index.1EndIf indexcopy = indexWend

Page 59: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 59 of 88

The following code does not change PR2, using only the middle value of 167, thus not trying to preserve an off time close to 20 ms.

On power-up the servo has been adjusted for no rotation. The encoder can be turned approximately 6 - 7 times CW to ramp the speed up to maximum, and similarly for CCW.

{servo3 HamStack with 18F46K22; DEV-1 board}Device = 18F46K22Config PLLCFG = OFF

#option SWORDFISH_SE = true // SFR fixPublic Macro write_sfr(sfr, val) #if (SWORDFISH_SE) WREG = val Asm movff WREG, sfr End Asm #else sfr = val #endifEnd Macro

Dim encoder_last_a As BitDim encoder_last_b As BitDim encoder_now_a As BitDim encoder_now_b As BitDim button_l As PORTE.0 ' Rotary encoder LeftDim button_r As PORTE.1 ' Rotary encoder RightDim index As ByteDim indexcopy As Byte

index = 47write_sfr(ANSELE, %11111100)OSCCON = %00100010CCP2CON = %00001100PR2 = 167CCP2CON.4 = index.0CCP2CON.5 = index.1indexcopy = indexCCPR2L = indexcopy >>2T2CON = %00000110TRISC.1 = 0

Page 60: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 60 of 88

While (TRUE)encoder_now_a = button_lencoder_now_b = button_rIf encoder_now_a <> encoder_last_a Or encoder_now_b <>encoder_last_b Then If encoder_last_a=1 And encoder_last_b=1 And encoder_now_a=0 And encoder_now_b=1 Then If (index > 41) Then Dec(index) EndIf EndIf If encoder_last_a=0 And encoder_last_b=1 And encoder_now_a=1 And encoder_now_b=1 Then If (index <53) Then Inc(index) EndIf EndIfEndIfencoder_last_a = encoder_now_aencoder_last_b = encoder_now_bIf (indexcopy <> index) Then indexcopy = index CCPR2L = indexcopy >>2 CCP2CON.4 = index.0 CCP2CON.5 = index.1 EndIf indexcopy = indexWend

Page 61: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 61 of 88

Compiler Setup

It is important to know about the 18F4620.bas and 18F46K22.bas Include files, which are installed when Swordfish is installed.

On my XP machine, these files are in C:\Program Files\Mecanique\Swordfish\IncludesOn my Windows 8 machine: C:\ProgramData\Mecanique\SwordfishSE\Includes

Unless overridden with Config statements, these Include.bas files set the initial power-up conditions for the chip, like which oscillator to use.

The 18F4620.bas Include file has these possible choices for the oscillator fuse:

// configuration fuses...public config OSC(OSC) = [LP, XT, HS, RC, EC, ECIO6, HSPLL, RCIO6, INTIO67, INTIO7],

It makes this choice for oscillator: // default fuses... config OSC = HS,

The choice of HS, High-Speed Crystal/Resonator, allows the HamStack 401 CPU kit to power-up using its 10 MHz crystal. (4620 DS pg. 25, 252).

The 18F46K22.bas Include file has these possible choices for the oscillator fuse:

// configuration fuses...Public Config FOSC(FOSC) = [LP, XT, HSHP, HSMP, ECHP, ECHPIO6, RC, RCIO6, INTIO67, INTIO7, ECMP, ECMPIO6, ECLP, ECLPIO6],

But in: // default fuses... config

no choice is made for FOSC. By default, the compiler chooses ECHP, EC oscillator (high power, >16 MHz),thereby expecting an external oscillator rather than a crystal. The HamStack 402 CPU kit therefore does not power-up using its 16 MHz crystal, and does nothing since there is no external oscillator attached. (46K22 DS pg. 27, 357).

We can overcome this problem by including this statement in code: Config FOSC = HSMP which will turn on the HS oscillator (medium power, 4 MHz-16 MHz), thereby using the 16 MHz crystal on the HamStack board.

Setup

Page 62: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 62 of 88

Sierra Radio Systems has a modified 18F46K22.bas Include file for download on their site under Support.

In that file we see: // default fuses... config ... FOSC = HSMP, PLLCFG = ON,

The oscillator is set to use the 16 MHz crystal and also to use the PLL so that the chip’s oscillator is 64 MHz at power-up.

In many examples, the PLL is turned off so as to have a 16 MHz clock, using this code statement:

Config PLLCFG = OFF

All Swordfish code in these notes assumes using the modified Include files from Sierra Radio Systems.

Page 63: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 63 of 88

My PICkit2 would not recognize an 18F46K22 until I upgraded its device file to version 1.62.14.

I put both of these files, downloaded from Microchip, in the same folder:

PK2V023200.hex PK2DeviceFile.dat

With the standalone PICkit2 application open, choose Download PICkit 2 Operating System and choose the above folder. After a minute the PICkit2 will be upgraded.

PICkit2

Page 64: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 64 of 88

The free version of the Swordfish Compiler has 2 limitations, RAM and SFR access:

“The SE version is limited to the amount of RAM available to the user. This is because no bank switching code is available in the SE version. Please note that some newer Microchip devices have Special Function Registers (SFRs) that reside outside of access RAM. The SE version will be unable to access these SFRs by default...”

(http://www.sfcompiler.co.uk/swordfish/download/index.html)

Some projects will require much or all of the available RAM so the commercial version of the compiler would be essential.

The PIC 18F46K22 unfortunately is one of those newer chips which has 40 of its SFRs outside of access RAM.

Working on my PIC18F4620 COOKBOOK Hands-on Experiments gave me familiarity with access RAM. Many instructions allow easy access to the SFRs, without memory bank switching.

For example, a BCF or Bit Clear f instruction can be used to clear a single bit, like in the TRISE SFR, by using the RAM access bit rather than bank switching (46K22 DS pg. 382). Below the instruction to clear bit 2 in TRISE is illustrated.

SFR Fix

f is byte sizeThe bit to be cleared has to be bit 0, bit 1, ...,bit 7

A single bit is set to 0 by this instruction

The RAM access bit determines if access RAM is used or bank switching

Page 65: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 65 of 88

The 1001, or 9 is the first nibble of the instruction.

The bbb determines which bit is cleared, 010, or bit 2 in this example.

The RAM access bit a, determines whether to use access RAM or bank switching. We want to use access RAM so a = 0. The second nibble then equals 0100, or 4

So the first byte of this BCF instruction is 94. The ffff ffff is the address of the appropriate SFR in access RAM.

46K22 DS pg. 83 shows the SFRs. The TRISE SFR is at address F96. Since we can use access RAM, we need only the 96.

ffff ffff = 96.

Therefore, the complete instruction to clear bit 2 in the TRISE register, and make pin E2 an output, is 9496. The Swordfish compiler will generate a 9496 as the output for the code line TRISE.2 = 0

On 46K22 DS pg. 80 it says:

Note 1: Addresses F38h through F5Fh are also used by SFRs, but are not part of the Access RAM. User must always use the complete address or load the proper BSR value to access these registers.

Therefore we cannot use an instruction like in the example above, setting a = 0.

Page 66: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 66 of 88

40 SFRs in the 18F46K22 cannot be loaded or read through access RAM type instructions, outlined in red below (46K22 DS pg. 83)

Page 67: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 67 of 88

The MOVFF, or Move f to f, is an unusual instruction in that it can copy the contents of any memory location from hex 000 to hex FFF (46K22 DS pg. 398).

Since all of the 18F46K22 SFRs are in this space, MOVFF should be able to load all SFRs, even those not in access RAM, without bank switching.

The source data could be loaded into the WREG (Working Register), an SFR which does live in access RAM, and then copied from there using the MOVFF instruction into any SFR.

Experiment #1: hand-assemble instructions to try and get CCP5 to output a square wave on pin E2.

MOVLW, 0x0C 0E0C // load the value 00001100 into WREGMOVFF, CCP5CON CFE8 // source is WREG at address FE8 FF54 // destination is CCP5CON at address F54MOVLW, 0x80 0E80 // load the value 10000000 into WREGMOVFF, CCPR5L CFE8 // source is WREG at address FE8 FF55 // destination is CCPR5L at address F55BCF, TRISE.2 9496 // make pin E2 an output, use access RAMMOVLW, 0x04 0E04 // load the value 00000100 into WREGMOVWF, T2CON 6EBA // load the value 4 into T2CON using access RAM

Page 68: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 68 of 88

Those nine instruction words should make a square wave at pin E2, using the default power-up value of PR2 = 255. Those instructions are basically equivalent to:

CCP5CON = %00001100 // enable PWM mode in CCP5 CCPR5L = 128 // set for 50% duty cycle TRISE.2 = 0 // make pin E2 an output T2CON = $00000100 // turn on Timer 2

There is no device declaration but the PICkit2 has already identified the target chip as an 18F46K22. There is no While/Wend code but once a CCP is set to output PWM, it should just keep going.

The oscillator has to be set manually to 0011 using the Configuration Word editor as described in the PIC18F4620 COOKBOOK.

Below shows the nine instruction words entered and then written to chip, and the result from a Saleae Logic probe.

oscillator

Page 69: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 69 of 88

Since those minimum instructions worked, it seemed like the following program should also work. It compiles without error but does not generate PWM on E2.

Device = 18F46K22Clock = 16Config PLLCFG = OFF

Asm movlw 0x0C movff w, CCP5CON // alternatively, movff w, F54 movlw 0x80 movff w, CCPR5L // alternatively, movff w, F55End Asm

TRISE.2 = 0T2CON = %00000100

While(true)Wend

Below are the instruction words written to chip, with no PWM generation. Nine of the instructions, underlined in red, are almost the same as the instructions which do work.

There are additional instructions like the first two words in the reset vector which jump over the interrupt vector positions at 0004 - 0007. The 8C18 is, to me, a mystery instruction, frequently but not always stuck in by the compiler; it can be FFFFed out with seemingly no consequences. And at the end is BRA code for an endless loop.

But the 2 MOVFF instructions are not correct. Both the first and second C000 should be CFE8. When these are edited to the correct values and written to chip, then there is PWM on E2.

So the free version of the compiler is not generating correct code for the first word of the MOVFF instruction. At least when the MOVFF instruction is using the WREG as the source register.

Page 70: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 70 of 88

What if a different SFR is used as a “temporary” register, like OSCTUNE?

Device = 18F46K22Clock = 16Config PLLCFG = OFFAsm movlw 0x0C // load WREG with 12 movwf 0x9b, 0 // copy into OSCTUNE register movff 0xf9b, 0xf54 // copy from OSCTUNE into CCP5CON movlw 0x80 // load WREG with 128 for 50% duty cycle movwf 0x9b, 0 // copy into OSCTUNE register movff 0xf9b, 0xF55 // copy from OSCTUNE into CCPR5LEnd AsmTRISE.2 = 0 // those two zeroes above in red are requiredT2CON = %00000100 // to tell the compiler to use theWhile(true) // RAM access bit a = 0Wend

The code above compiles and outputs PWM on pin E2, at the expense of extra instructions to not use the WREG with the MOVFF instruction.

In this specific case, no harm is done using the OSCTUNE register; because the chip is using its external crystal, this register has no effect on chip operation. Other SFRs like TMR1L will also work.

Using SFRs in this unintended way, obviously bad programming practice, can work OK with such simple experiments. But there may be unintended consequences when not paying close attention to a specific register.

For example, why not use T2CON as a temporary storage place since it gets correctly set at the end anyway?

Asm movlw 0x0C // load WREG with 12 movwf 0xba, 0 // copy into T2CON register movff 0xfba, 0xf54 // copy from T2CON into CCP5CON movlw 0x80 // load WREG with 128 for 50% duty cycle movwf 0xba, 0 // copy into T2CON register movff 0xfba, 0xF55 // copy from T2CON into CCPR5LEnd AsmTRISE.2 = 0T2CON = %00000100

Page 71: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 71 of 88

But, the code above outputs no PWM on pin E2. Looking at the bit settings for the T2CON register shows why (46K22 DS pg. 135).

Bit 7 is unimplemented and always read as 0. Trying to load 80 into the T2CON register fails to set bit 7, and 0 is then copied into the CCPR5L register, resulting in 0% duty cycle.

Changing movlw, 0x80 to movlw 0x7F will give a duty cycle as close as possible to 50%.

Therefore, the down side of using T2CON as a temporary storage register is that the only possible duty cycles are < 50%.

Hunting through the Swordfish wiki looking for an easier way finally turned up an answer, in the entry entitled: “Bug with SwordfishSE PIC18F46K22? (works with PICBASIC PRO), Dec 27, 2011”

If you include the following #option switch and macro, you can load values to those SFRs outside of access RAM and also read from them.

// set this option true if building with the SE compiler#option SWORDFISH_SE = true//// these macros are used to read and write SFR's that are located in the upper// bank (bank 15), but are not part of the access bank//// since SE does not contain code to set the bank select register, setting// #option SWORDFISH_SE true will enable the write_sfr()/read_sfr() macros to// generate a MOVFF instruction so that setting the BSR is not required//

Page 72: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 72 of 88

Public Macro write_sfr(sfr, val)#if (SWORDFISH_SE) WREG = val Asm movff wreg, sfr End Asm#else sfr = val#endifEnd Macro

Public Macro read_sfr(sfr, bvar)#if (SWORDFISH_SE) Asm movff sfr, bvar End Asm#else bvar = sfr#endifEnd Macro

Included in a module called SetDigitalIO is the following code for the 18F46K22

...#elseif _device in (18F43K22, 18F44K22, 18F45K22, 18F46K22) // added V1.5// registers $0F38 - $0F5F are not part of the access bankwrite_sfr(ANSELA, $00)write_sfr(ANSELB, $00)write_sfr(ANSELC, $00)write_sfr(ANSELD, $00)write_sfr(ANSELE, $00)CM1CON0 = %00001000 // changed V1.6CM2CON0 = %00001000VREFCON1 = $00 // added V1.6...

Page 73: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 73 of 88

These five instructions clear the ANSEL registers:

write_sfr(ANSELA, $00) write_sfr(ANSELB, $00) write_sfr(ANSELC, $00) write_sfr(ANSELD, $00) write_sfr(ANSELE, $00)

which were configured as analog input pins on power-up ( bits = 1)

In order to have pins with analog functions be digital inputs instead, the appropriate ANSEL bits need to be cleared.

The SetDigitalIO module clears all of them at once, so no pins are acting as analog inputs, which may or may not be what you need.

The instructions:

CM1CON0 = %00001000 // changed V1.6 CM2CON0 = %00001000

load the power-up default condition of those registers (46K22 DS pg. 317), with the comparator OFF (bit 7 = 0).

The comparator inputs and outputs are on 8 pins, A0 - A5, B1, and B3. If previous code had turned comparator functions on, then setting all pins as digital might cause out-of-spec current consumption on those pins (46K22 DS pg. 315). I don’t however understand the point of setting bit 3 (1 = Cx operates in Normal-Power, Higher Speed mode) since the comparator is off.

Likewise, the instruction:

VREFCON1 = $00 // added V1.6

loads the power-up condition of that register with the DAC off, pin A2 (46K22 DS pg. 347), presumably since previous code might have A2 configured as a DAC output.

Page 74: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 74 of 88

Then, to my amazement, I discovered that Sierra Radio had already included these macros and option switch into hs_utils.bas.

And, then I found that the SetDigitalIO Library module is directly available for download on the Swordfish Wiki under Swordfish User / Modules / Updated Modules.

In conclusion, there are then at least 3 ways to fix SFR problems:

a. “hard load” via assembly language, avoiding WREG as a temporary register. It will be necessary to avoid using many other SFRs as temporary registers. This method is only for simple experiments.

b. Use the #option SWORDFISH_SE = true switch and the Library file “hs_utils.bas” to remove the analog input function from all applicable pins. Other Library files such as “hs_lcd.bas” include the “hs_utils.bas” Library file.

This strategy makes the macro available for writing to additional SFRs not in Access RAM including those SFRs already written to (ANSELA, ...).

c. Include the option switch and macro in your code to specifically set SFRs, like CCP5CON (for example the code: BasicPWM3). It is kind of redundant to include the macro code if any Library file is included which already has the macro code in it.

Page 75: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 75 of 88

DEV-1 LCD

{LCD1: HamStack with 18F46K22 and DEV-1 board}Device = 18F46K22Clock = 16Config PLLCFG = OFF // chip clocked at 16 MHz

#option SWORDFISH_SE = true // enables use of macro in the Library // file “hs.utils.bas”which is called#option LCD_DATA = PORTD.0 // by “hs_lcd.bas”. That macro is#option LCD_RS = PORTD.4 // used to turn off analog input and#option LCD_EN = PORTD.5 // make all pins digital, including the // PORTD pins, so that now the LCDInclude "hs_lcd.bas" // will work.Include "hs_convert.bas"

Dim counter As Bytecounter = 0

WriteAt(1,1, " binary dec hex")While true WriteAt(2,1,BinToStr(counter,8)," ",DecToStr(counter,3), " ", HexToStr(counter,2)) DelayMS(400) Inc(counter)Wend

(LCD1: HamStack with 18F46K22 and DEV-1 board}Device = 18F46K22Clock = 16//Config PLLCFG = OFF // chip clocked at 64 MHz

If however, the Config statement is removed or commented out, then the chip is clocked at 64 MHz and the DEV-1 LCD no longer works.

This is because the signals going to the LCD to initialize it and send data are too fast.

Page 76: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 76 of 88

{LCD2: HamStack with 18F46K22 and DEV-1 board}Device = 18F46K22Clock = 16//Config PLLCFG = OFF // chip clocked at 64 MHz

#option LCD_Command_US = 3135 // max 65535 default 2000#option LCD_Data_US = 150 // max 255 default 50#option LCD_INIT_Delay = 300 // max 1000 default 100

#option SWORDFISH_SE = true

#option LCD_DATA = PORTD.0#option LCD_RS = PORTD.4#option LCD_EN = PORTD.5

Include "hs_lcd.bas"Include "hs_convert.bas"

Dim counter As Bytecounter = 0

WriteAt(1,1, " binary dec hex")While true WriteAt(2,1,BinToStr(counter,8)," ",DecToStr(counter,3), " ", HexToStr(counter,2)) DelayMS(400) Inc(counter)Wend

Those options statements in red above slow down the LCD signals from the default values which work at 16 MHz.

The specific values, i.e. 3135, were found by trial and error to just barely work with the chip clocked at 64 MHz. The values may need to be increased more.

However, the compiler no long knows that the chip is being clocked at 64 MHz so the DelayMS routines are not correct; the 1 second delay has become one quarter second.

Changing the clock statement to: Clock = 64 fixes this.

The Swordfish Help says that “the built in routines will only currently generate the correct timings for a certain range of frequencies. These include: 3.58, 4 to 13, 14.32, 15 to 40 and 64 MHz.”

Page 77: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 77 of 88

{LCD3: HamStack with 18F46K22 and DEV-1 board}Device = 18F46K22Clock = 16Config PLLCFG = OFF // chip clocked at 16 MHz

#option LCD_DATA = PORTD.0#option LCD_RS = PORTD.4#option LCD_EN = PORTD.5

Include "hs_lcd.bas"Include "hs_convert.bas"Dim counter As Byte

Asm movlw 0xc0 // %11000000 movwf 0xce, 0 // TMR1L as temp register movff 0xfce, 0xf3b // set ANSELD bits 0-5 digitalEnd Asm

counter = 0WriteAt(1,1, " binary dec hex")

While true WriteAt(2,1,BinToStr(counter,8)," ",DecToStr(counter,3), " ", HexToStr(counter,2)) DelayMS(400) Inc(counter)Wend

Above is a demonstration of the “hard load” method described in SFR Fix.

It turns the analog function off only for bits 0 - 5 on PORTD, since those are the only pins being used by the LCD.

If the line #option SWORDFISH_SE = true had been included, then the assembly language would be unnecessary unless used to set other SFRs.

Page 78: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 78 of 88

The final program, just for fun, demonstrates several things:

1) Using the Swordfish LCD Programmable Chr Generator Plugin.2) Writing custom characters into the LCD character generator RAM (CGRAM)3) Writing new custom characters into CGRAM ‘on the fly’.4) Modifying the hs_lcd.bas Library file to include 4 new commands; these are minor

modifications. The modified file is called: hs_lcdmod.bas.a. cmdDisplayOff turns the LCD display OFFb. cmdDisplayOn turns the LCD display ONc. cmdShiftLeft shifts the display to the leftd. cmdShiftRight shifts the display to the right

(5) Writing characters into Display Data RAM (DDRAM) off the display and then shifting them in. Each line has 40 characters of which 16 are displayed at one time.

There is a handy Plugin available on the Swordfish site under Swordfish User/Plugins called: LCD Programmable Chr Generator. It allows easy construction of 8 custom LCD characters and pastes the necessary Const code into your program (I’ve run this only under Windows XP).

Below are screenshots of the 3 sets of custom characters used in this program.

Page 79: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 79 of 88

Below, in red, are the four lines added to the hs_lcd.bas Library file. A copy of the original file was opened in WordPad, and then saved as hs_lcdmod.bas so I would not confuse it with the original. It was then copied into the Swordfish Library folder and Swordfish closed and reopened.

// public command constants...public const cmdCGRAM = %01000000, cmdDDRAM = %10000000, cmdClear = %00000001, cmdHome = %00000010, cmdCursorOff = %00001100, cmdCursorOn = %00001110, cmdBlinkOn = %00001101, cmdBlinkOff = %00001100, cmdMoveLeft = %00010000, cmdMoveRight = %00010100, cmdShiftLeft = %00011000, cmdShiftRight = %00011100, cmdDisplayOff = %00001000, cmdDisplayOn = %00001100

// bring delay options into the module...const

There are many data sheets available for the Hitachi HD44780 chip with its instruction set. For example, as shown below, the instruction to shift the cursor or display is, reading 8 bits from the right: 0 0 0 1 S/C R/L X X. On another page is decoding for the S/C and R/L. We want S/C = 1 so the display is shifted, and R/L = 0 so the shift is to the left, making the final instruction 0 0 0 1 1 0 00 (where the two LSBs don’t matter).

Page 80: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 80 of 88

{LCD4: HamStack with 18F46K22 and DEV-1 board}Device = 18F46k22Clock = 16Config PLLCFG = off

#option SWORDFISH_SE = true // SFR fix#option LCD_DATA = PORTD.0#option LCD_RS = PORTD.4 #option LCD_EN = PORTD.5

Const box(64) As Byte = ($1F,$11,$11,$11,$11,$11,$11,$1F, //(0) $00,$0E,$0A,$0A,$0A,$0A,$0E,$00, //(1) $00,$00,$04,$04,$04,$04,$00,$00, //(2) $00,$00,$00,$04,$04,$00,$00,$00, //(3) $00,$00,$00,$00,$00,$00,$00,$00, //(4) $00,$00,$00,$04,$04,$00,$00,$00, //(5) $00,$00,$04,$04,$04,$04,$00,$00, //(6) $00,$0E,$0A,$0A,$0A,$0A,$0E,$00) //(7) Const press(64) As Byte = ($1F,$00,$00,$00,$00,$00,$00,$1F, //(0) $00,$1F,$00,$00,$00,$00,$1F,$00, //(1) $00,$00,$1F,$00,$00,$1F,$00,$00, //(2) $00,$00,$00,$0E,$0E,$00,$00,$00, //(3) $00,$00,$00,$00,$00,$00,$00,$00, //(4) $00,$00,$00,$0E,$0E,$00,$00,$00, //(5) $00,$00,$1F,$00,$00,$1F,$00,$00, //(6) $00,$1F,$00,$00,$00,$00,$1F,$00) //(7) Const pwmchar(64) As Byte = ($0E,$0A,$0A,$0A,$0A,$0A,$0A,$1B, //(0) $1B,$0A,$0A,$0A,$0A,$0A,$0A,$0E, //(1) $1F,$11,$11,$11,$11,$11,$11,$11, //(2) $11,$11,$11,$11,$11,$11,$11,$1F, //(3) $04,$04,$04,$04,$04,$04,$04,$1F, //(4) $1F,$04,$04,$04,$04,$04,$04,$04, //(5) $00,$00,$00,$00,$00,$00,$00,$1F, //(6) $1F,$00,$00,$00,$00,$00,$00,$00) //(7)

Const PatternArray(40) As Byte = (6,6,6,0,6,0,2,0,4,4,0,0,0,0,6,6,6,6,6,0,6,6,2,6,6,2,6,6,0,0,0,0,6,6,4,4,4,4,6,6)Include "hs_lcdmod.bas"Dim index1 As ByteDim index2 As Byte

Page 81: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 81 of 88

While trueClsWriteAt(1,1," HamStack ")WriteAt(2,1," DEV-1 Board ")DelayMS(1200)

For index2 = 0 To 1 LCD.Write(box) // load custom “box” characters For index1 = 0 To 7 // into CGRAM MoveCursor(1,1) LCD.Write(index1) // sequentially display these new MoveCursor(1,2) // characters at 6 LCD locations LCD.Write(index1) MoveCursor(1,3) LCD.Write(index1) MoveCursor(1,14) LCD.Write(index1) MoveCursor(1,15) LCD.Write(index1) MoveCursor(1,16) LCD.Write(index1) DelayMS(200) Next LCD.Write(press) // load custom “press” characters For index1 = 0 To 7 // into CGRAM MoveCursor(1,1) LCD.Write(index1) // sequentially display these new MoveCursor(1,2) // characters at 6 LCD locations LCD.Write(index1) MoveCursor(1,3) LCD.Write(index1) MoveCursor(1,14) LCD.Write(index1) MoveCursor(1,15) LCD.Write(index1) MoveCursor(1,16) LCD.Write(index1) DelayMS(200) NextClsNext

Page 82: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 82 of 88

WriteAt(1,1,"M P")DelayMS(100)WriteAt(1,1,"WM PI")DelayMS(100)WriteAt(1,1,"PWM PIC")DelayMS(100)WriteAt(1,1," PWM PIC ")DelayMS(100)WriteAt(1,1," PWM PIC ")DelayMS(100)WriteAt(1,1," PWM PIC ")DelayMS(100)WriteAt(1,1," PWM PIC ")DelayMS(100)WriteAt(1,1," PWMPIC ")DelayMS(100)WriteAt(1,1," PWIC ")DelayMS(100)WriteAt(1,1," PC ")DelayMS(100)WriteAt(1,1," PP ")DelayMS(100)WriteAt(1,1," PIPW ")DelayMS(100)WriteAt(1,1," PICPWM ")DelayMS(100)WriteAt(1,1," PIC PWM ")DelayMS(500)WriteAt(2,1,"18F4620 18F46K22")DelayMS(1700)For index1 = 1 To 2 LCD.Command(cmdDisplayOff) // turn display on and off DelayMS(700) LCD.Command(cmdDisplayOn) DelayMS(700)Next WriteAt(1,1," ")LCD.Write(pwmchar) load custom “pwmchar”characters

Page 83: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 83 of 88

MoveCursor(2,1) For index1 = 0 To 39 LCD.Write(PatternArray(index1)) // display custom “pwmchar”Next // characters into 40 LCD // positions, visible and off-For index1 = 1 To 40 // screen according to numbers LCD.Command(cmdShiftLeft) // in PatternArray DelayMS(100)NextFor index1 = 1 To 30 LCD.Command(cmdShiftRight) DelayMS(100)Next Wend

Page 84: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 84 of 88

On the Swordfish page under Swordfish User / Modules there is a PWM module written by David Barker, who started Mecanique.

This module is very easy to download and use; it does work with the 18F4620 and 18F46K22 running at 10 and 16 MHz.

In this module is a reference: “ From an idea found at http://www.eng-serve.net/pic”, an interesting site with a calculator of values for the various registers.

Playing with these chips and registers enabled me to understand this module, and also how difficult it would be (at least for me) to code a comprehensive PWM module which could handle multiple frequencies, CCPS, and duty cycles, maintaining good results across such wide ranges.

PWM Module

{PWMmodule1: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF

Include “PWM.bas”

SetFreq(5000, 50) // 5000 Hz = period; 50 = duty cycle

While(true)Wend

The PWM module depends on the Clock statement. The frequency input must be less than 65536 although that can be raised by modifying the module, making some Words into LongWords. If a value of 65536 or greater is entered, the compiler will throw the error: “Constant expression violates subrange bounds.”

However, if a frequency of 972 is entered, the program will compile but produce no PWM, whereas a frequency of 973 will. This can be verified by doing the actual calculations in the module by hand.

Page 85: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 85 of 88

input frequency Hz

input duty cycle %

measuredfrequency Hz

measuredduty cycle %

972 50 0 0

973 50 976 50.0

973 1 976 0.8

65535 50 65574 49.2

65535 2 65574 1.6

65535 1 0 0

Below are samples clocking the 18F46K22 at 32 kHz, using its internal oscillator configured with: OSCCON = %00000010.

Since the module calculates based on the Clock statement, this experiment had this line:

Clock = 0.032

This is not a fair use of this module as it was designed around chips running at 20 MHz.

input frequency Hz

input duty cycle %

measuredfrequency Hz

measuredduty cycle %

1 50 0 0

2 1 1.9 0.8

2 50 1.9 49.8

2666 33 0 0

2666 34 2.5 33.3

2666 50 2564 33.3

2667 50 0 0

Page 86: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 86 of 88

Although an unlikely use of a USART, it can output a decent square wave from its TX pin, with a limited range of possible frequencies, making it easy to test that pin and scope it. Additionally, it could serve as a ‘quick and dirty’ pulse generator.

{USART1: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF // clocked at 16 MHz

SPBRG = 128 // load the baud rate generator registerRCSTA.7 = 1 // set Serial Port Enable bitTXSTA.5 = 1 // set Transmit Enable bit // scope pin 25, C6, TXWhile true TXREG = 85 // capital U, 01010101Wend

Capital ‘U’ has the bit pattern 01010101 (adding a zero for the MSB, 55 hex, 85 decimal).

Default config: 1 start bit, 1 stop bit, 8 data bits, no parity (10 symbols per transmission).Default Tx signal states: idle = High, start bit = Low, stop bit = High.

With the TXREG continuously reloaded with 55 hex, a capital ‘U’ would get shifted out like this: ...idle .... 01010101010101 ... The stop/start bits just add another pulse to the train.

Transmitting only capital ‘U’s, there are 256 possible square wave frequencies, six of which would be legitimate baud rates (although some would have too much error). Simply change the value in SPBRG from 0 to 255.

SPBRG = 0 frequency = 125 kHz SPBRG = 1 frequency = 62.5 kHz SPBRG = 10 frequency = 11.4 kHz! SPBRG = 128 frequency = 969 Hz SPBRG = 255 frequency = 488 Hz

USART PWM

Page 87: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 87 of 88

A lowest frequency of approximately .9 Hz can be obtained by adding a line to use the slowest internal oscillator at approximately 32 kHz:

! ! OSCCON = %00000010

The TX output looks like below, captured using the PICKIT 2 as a logic tool. In this picture the pulses do not look completely regular but they do on a Saleae Logic probe.

Pulse GeneratorAnother (no doubt equally unlikely) use of the USART would be as a pulse generator, either high or low, with a non-continuous adjustable pulse width from approximately 500 ms (clock chip at 32 kHz) to 1 µs (clock chip at 64 MHz)

{USART2: HamStack with 18F46K22; DEV-1 board optional}Device = 18F46K22Clock = 16Config PLLCFG = OFF // clocked at 16 MHz

OSCCON = %00000010 // use internal oscillator at ̃32 KHzSPBRG = 255 // slowest pulseRCSTA.7 = 1 // set Serial Port Enable bitTXSTA.5 = 1 // set Transmit Enable bit // scope pin 25, C6, TXWhile true TXREG = %00000000 Wend

idle start bit0 bit1 bit 2 bit 3 bit 4 bit5 bit 6 bit 7 stop start bit 0 bit 1 bit 2 bit 3 bit 4 ....! LSB!! ! ! ! ! MSB! ! LSB

Page 88: PIC PWM - hamstack.comPIC PWM 9/16/13!! ! ! ! ! ! ... These notes demonstrate generating PWM on the HamStack platform using the PIC 18F4620 and the PIC 18F46K22. ... Asm

kj6hfr! PIC PWM 9/16/13! ! ! ! ! ! ! page 88 of 88

How square is the square wave?

(46K22 DS pg. 269) ...“The heart of the transmitter is the serial Transmit Shift Register (TSR), which is not directly accessible by software. The TSR obtains its data from the transmit buffer, which is the TXREGx register.”...

...“If the TSR still contains all or part of a previous character, the new character data is held in the TXREGx until the Stop bit of the previous character has been transmitted. The pending character in the TXREGx is then transferred to the TSR in one TCY immediately following the Stop bit transmission. The transmission of the Start bit, data bits and Stop bit sequence commences immediately following the transfer of the data to the TSR from theTXREGx.”...

That makes me think that there would be an extra instruction cycle, TCY, after every stop bit, making every Start bit slightly longer, and the signal not quite square.

And yet, FIGURE 16-4: ASYNCHRONOUS TRANSMISSION (BACK-TO-BACK) (pg. 271) shows the TSR getting loaded with a subsequent byte during the Stop bit, not after.

Instruction cycle period (TCY) equals four times the input oscillator time base period for all configurations except PLL ( 46K22 DS pg. 449).

But, try as I might, I cannot capture any periodic added time on any cycle.