|
|
View previous topic :: View next topic |
Author |
Message |
Sergeant82d
Joined: 01 Nov 2009 Posts: 55 Location: Central Oklahoma
|
(SOLVED) ECCP Full-Bridge PWM x 2 w/ 18F45K22 |
Posted: Tue Oct 19, 2010 9:43 pm |
|
|
I am trying to get both full-bridge PWM outputs working on an 18F45K22. Using all the usual suspects (setting TRISD directly, writing to CCP1CON, PWM1CON, ECCP1AS, etc.) - in addition to just "setup_ccp1" - I have not been able to get both outputs working. PWM 2 works fine, but on PWM1, only the P1A & P1C pins are toggling with direction changes - no actual PWM output on P1B & P1D.
I believe, based on the ECCP1AS value (see below), that my problem is related to that register, but even though I have tried writing it directly I have not had any luck.
I am afraid I am at a complete loss while looking at the .lst file. I copied the assembly code and tried to use it directly, changing what I *thought* was wrong, but I do not know assembly at all, and was unsuccessful.
I am using Ver 4.110 under MPLAB 8.56 on Windows XP Pro, in hardware. I am using a scope & LEDs to monitor pin status.
I have searched the forums and the web:
http://www.ccsinfo.com/forum/viewtopic.php?t=43129&highlight=eccp
and although I tried everything in the above post - and many more - I could never get Timer 2 to work at all. Both Timers 4 & 6 work fine for me, except as stated above.
I stripped out the printf commands for posting, but here are the register values I have been printing from this code during my testing:
Forward:
PWM1CON = 183
PWM2CON = 101
CCP1CON = 189
CCP2CON = 102
ECCP1AS = 182
ECCP2AS = 100
Reverse:
PWM1CON = 183
PWM2CON = 101
CCP1CON = 189
CCP2CON = 102
ECCP1AS = 182
ECCP2AS = 100
Here is a test program:
Code: |
#include <18F45K22.h>
#fuses INTRC_IO, NOWDT, NOPROTECT
#fuses NOLPT1OSC, MCLR, NOPBADEN, CCP2C1, CCP2D2
#fuses NOPUT, NOFCMEN, NOIESO
#fuses NOXINST, NOSTVREN, NODEBUG
#fuses NOBROWNOUT, NODELAYINTOSC
#fuses NOEBTR, NOEBTRB, NOWRTC
#use delay ( clock=64M )
#use rs232 ( uart1, baud=115200 )
//////////////////////////////////////////////////////////////////////////////////////////
#define CCP1CON 0xFBD
#define CCP2CON 0xF66
#define T2CON 0xFBA
#define PR2 0xFBB
#define CCPR1L 0xFBE
#define CCPTMRS0 0xF49
#define CCPTMRS1 0xF48
#define ECCP1AS 0xFB6
#define ECCP2AS 0xF64
#define PWM1CON 0xFB7
#define PWM2CON 0xF65
#define CM1CON0 0xF79
#define CM2CON0 0xF78
#define CM2CON1 0xF77
//////////////////////////////////////////////////////////////////////////////////////////
void Initmcu(void)
{
setup_timer_6( T6_DIV_BY_16, 99, 1 ); // 10 KHz PWM at 64 MHz Oscillator Speed
*CCPTMRS0 = 0x92; // 0x92 for Timer 6; 0x49 for Timer 4
setup_comparator(NC_NC_NC_NC);
setup_adc_ports(NO_ANALOGS|VSS_VDD);
setup_adc(ADC_OFF|ADC_TAD_MUL_0);
setup_spi(SPI_SS_DISABLED);
SET_PWM1_DUTY(85);
SET_PWM2_DUTY(75);
}
//////////////////////////////////////////////////////////////////////////////////////////
void main()
{
Initmcu();
while (true)
{
SETUP_CCP1( CCP_PWM_FULL_BRIDGE | CCP_PWM_H_H );
SETUP_CCP2( CCP_PWM_FULL_BRIDGE | CCP_PWM_H_H );
set_tris_d(0x00);
delay_ms(1500);
SETUP_CCP1( CCP_PWM_FULL_BRIDGE_REV | CCP_PWM_H_H );
SETUP_CCP2( CCP_PWM_FULL_BRIDGE_REV | CCP_PWM_H_H );
set_tris_d(0x00);
delay_ms(1500);
}
}
|
I have been changing MCUs around quite a bit lately, trying to get one to work... I was able to get all three ECCP PWM ports working on an 87K22, but I can not get the oscillator to run above 16 MHz, regardless of what I do.
Any help would be much appreciated!
Brad
Last edited by Sergeant82d on Wed Oct 27, 2010 7:27 am; edited 1 time in total |
|
|
Sergeant82d
Joined: 01 Nov 2009 Posts: 55 Location: Central Oklahoma
|
#Byte vs. #Define - Important? |
Posted: Mon Oct 25, 2010 5:43 pm |
|
|
What are the practical differences between using #byte and #define?
I have tried both, and other than #byte taking up more memory space, I get different values when I print the registers (in the post above).
When I use #define, I get values in the 180 (decimal) range and anything I try to write to the registers does not "take"... using #byte, I get whatever value I set.
In this printout, I have set:
PWM1CON = 0X80;
ECCP1AS = 0x0F;
and I get the below values. Compare them, please, to the values in the original post:
Forward: PWM1CON = 128
PWM2CON = 0
CCP1CON = 76
CCP2CON = 76
ECCP1AS = 15
ECCP2AS = 0
Reverse: PWM1CON = 128
PWM2CON = 0
CCP1CON = 204
CCP2CON = 204
ECCP1AS = 15
ECCP2AS = 0
The CCPxCON register values are correct for the mode and direction; PWM2CON & ECCP2AS I did not set; as you can see, when using #byte instead of #define, I see the expected values.
Unfortunately, the PWM outputs on P1B & P1D are still not working.
Don't know if or how they are related to my not getting P1B & P1D working, but I have seen this type of behavior in other code/chips here, and wanted to ask...
Thanks for looking,
Brad |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Tue Oct 26, 2010 2:25 am |
|
|
They are _totally_ different......
A bit like saying what is the difference between a screwdriver, and a hammer....
#define, is a simple MACRO. It is part of the basic macro language of C, saying 'when you see this word, substitute this'. You can do some really nifty things with this, since it does support some rarely used features for testing, but it really has nothing to do with the core compiler, just being a 'pre compiling' text substitution.
#byte, is a compiler command, saying 'create a single character variable, and place this variable _at_ the defined location' (it has a second more rarely used meaning, if you define a larger variable first - int16 say, then use _this_ variable in a byte declaration, it relocates the existing variable to the new location). The first meaning is what is involved here though.
In either case, it is putting an actual variable in memory _at_ the defined location.
Now, if you use (say):
#byte FRED=0x80
You now have a variable called FRED, located _at_ memory location '0x80' in the RAM. If you choose a location that corresponds to a physical processor register, then reading and writing this variable, will access the actual register.
If instead you use:
#define FRED 0x80
Then all you have is a text replacement, that if you type 'FRED', the number 0x80 will be substituted.
So if you then use:
val=FRED;
All that will happen is that 0x80, will be put into val....
You can actually access the contents of the address 0x80, by using the syntax:
val=*((int8 *)FRED);
Which says "treat the number 0x80 (FRED) as the _address_ of an int8 variable, and read it's contents". A lot more work.
Obviously, accessing a physical variable takes more code than just substituting a number, which is why #byte takes more space than #define. It is actually doing something!......
When you use #define, you are doing nothing whatsoever with the real registers.
Best Wishes |
|
|
Sergeant82d
Joined: 01 Nov 2009 Posts: 55 Location: Central Oklahoma
|
|
Posted: Tue Oct 26, 2010 8:15 am |
|
|
Thank you. I had read the descriptions in help files and other texts, but thought for some reason that using a pointer to the #define'd register would still access it.
Thanks for clearing that up for me! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19504
|
|
Posted: Tue Oct 26, 2010 9:25 am |
|
|
Potentially using a pointer 'to' a value will access it's contents, _but_, doing this, you are not saying how large the thing 'pointed to' is. This then means that you can end up talking to other addresses nearby that you don't expect - hence the need to explicitly 'cast' the pointer to be to a byte or int8, if disastrous results are not to happen....
Remember also that the pointer needs to be used for both reading and writing. I suspect you were missing this at some point, and hence getting the defined value, not the contents of the value.
You also should get into the 'habit' of bracketing defines. So:
#define CCP1CON (0xFBD)
This is 'often forgotten', and can lead to disasters, with the result when the expansion takes place, not being syntactically what is expected.....
Best Wishes |
|
|
Sergeant82d
Joined: 01 Nov 2009 Posts: 55 Location: Central Oklahoma
|
|
Posted: Tue Oct 26, 2010 1:15 pm |
|
|
I noticed how you cast the pointer as an (int8 *), and figured it was something like that...
Looking at the values I was receiving, and doing a decimal/hex/binary conversion and comparing that to the register addresses, shows me that what you said is exactly what happened.
Thanks again!
****
You don't have any suggestions on my PWM problem, do you?
<grin>
Brad |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Oct 26, 2010 2:39 pm |
|
|
I'm the only one working on the pwm as far as I know. But anyway, here's what I came up with:
I think I made enough changes so vs. 4.110 will fit the Full Bridge section
of this diagram in the 18F45K22 data sheet:
Quote: | FIGURE 14-6: EXAMPLE PWM (ENHANCED MODE) OUTPUT ELATIONSHIPS (ACTIVE-HIGH STATE)
|
To make it work, I had to make my own setup_timer_2() routine and call
it instead the CCS routine. Also, the setup_ccp2() routine clobbers the
T2CON register (for Timer2), and therefore you must call the routine
to setup Timer 2 after setup_ccp2(). That way, T2CON remains
programmed correctly. I had to manually set the TRIS on the upper
3 bits of each set of Full Bridge pins.
Code: |
#include <18F45K22.h>
#fuses INTRC_IO,NOWDT,MCLR,PUT,NOPBADEN,NOPLLEN
#use delay (clock=4000000)
#define P1A PIN_C2
#define P1B PIN_D5
#define P1C PIN_D6
#define P1D PIN_D7
#define P2A PIN_C1
#define P2B PIN_D2
#define P2C PIN_D3
#define P2D PIN_D4
#byte T2CON = 0xFBA
#byte PR2 = 0xFBB
#define my_setup_timer_2(prescaler, PR2val, postscaler) \
if(prescaler) \
{ \
PR2 = PR2val; \
T2CON = prescaler | ((postscaler -1) << 3); \
} \
else \
T2CON = 0;
//===============================
void main()
{
setup_ccp1(CCP_PWM_FULL_BRIDGE | CCP_PWM_H_H);
output_low(P1B); // Manually set TRIS on these pins
output_low(P1C);
output_low(P1D);
setup_ccp2(CCP_PWM_FULL_BRIDGE | CCP_PWM_H_H);
output_low(P2B); // Manually set TRIS on these pins
output_low(P2C);
output_low(P2D);
my_setup_timer_2(T2_DIV_BY_1, 255, 1); // Must be after setup_ccpx()
set_pwm1_duty(100);
set_pwm2_duty(200);
while(1);
}
|
|
|
|
Sergeant82d
Joined: 01 Nov 2009 Posts: 55 Location: Central Oklahoma
|
|
Posted: Wed Oct 27, 2010 4:05 am |
|
|
Thanks! I'll try it when I get home tomorrow and can get hands on my board again.
*********************
EDIT:
IT WORKS!!!
THANK YOU THANK YOU THANK YOU!!! Now I can finally get on to the meat and potatoes of my project!
You are SO the man! |
|
|
mcr1981
Joined: 27 Oct 2010 Posts: 28
|
|
Posted: Wed Oct 27, 2010 2:25 pm |
|
|
PCM programmer wrote: |
To make it work, I had to make my own setup_timer_2() routine and call
it instead the CCS routine.
Code: |
#byte T2CON = 0xFBA
#byte PR2 = 0xFBB
#define my_setup_timer_2(prescaler, PR2val, postscaler) \
if(prescaler) \
{ \
PR2 = PR2val; \
T2CON = prescaler | ((postscaler -1) << 3); \
} \
else \
T2CON = 0;
//===============================
void main()
{
my_setup_timer_2(T2_DIV_BY_1, 255, 1); // Must be after setup_ccpx()
while(1);
}
|
|
Hi.
Looks like i'm not alone on this PWM thing with the PIC18F45k22.
I don't own CCS's IDE, i do it in MPLAB and I also find that problem with the functions for TIMER2 of CCS.
First I couldn't figure it out, and then, when I started looking at the assembler, something was not right.
I used the 45k20 and the PWM was fine. Then after comparing the two datasheets and looking at the address, the Complier did not change the address for the 45k22.
Started looking more into it (I own PICBASIC PRO and know a little how it makes the code) and found out that in the SFR file in the compiler directory, PR2 and T2CON are defined correctly but the assembler takes the address of a 45k20.
Already sent this to CCS, will have to wait for the response. |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|