Hi every body, and happy new year to all keil users ;) I am using the C515C with keil v2.39, I want to generate a PWM signal using one of the pins 1.0,1.1,1.2 or 1.3, I read the manual, put I found it difficult, because I have to configure different things. Like the following parameters:
T2CON |= 0x10; ET2=1; EXEN2=0; CRCL = 0x22; CRCH = 0xFF; EX3=0; CCL1 = 0xFF; CCH1 = 0x22; EX4=0; CCL2 = 0x00; CCH2 = 0x00; EX5=0; CCL3 = 0x000; CCH3 = 0x000; EX6=0; CCEN = 0xAA;
What I know, is that we have to use compare mode 0.
But how can I set T2 to be ready for all things, and how I change the duty cycle while from the program results...
Is there any ready function that has one input which is the duty cycle?
And thank you for your time.
Thank you Mr. Andy...
I have no problem in posting the code, if i have a code for the PWM...
My program calculates digital values and then i need to convert them to analog, using RC circuit after the PWM signal...
I don't need a very complicated specifications in the PWM signal, But in the programming I should pass through different things like disabling this and enabling that.. And then do this,
I need one pin that gives me a PWM signal and the duty cycle say it is between 0.5 to 1 second...
Thank you again for your time, and forgive me I am really a beginner in timers things.. thanks alot...
The duty cycle of a PWM signal is the proportion of the time when it is in one state compared to the other state.
So, you can't say that a duty cycle is in seconds. You either have to specify two times, or specify the duty cycle as a percentage, fraction or quota. In short, the duty cycle does not have a unit.
A period on the other hand do have a unit, since it is a measurement of time.
Doesn't the datasheets and the application notes for the chip contain the relevant information how to configure and use the PWM unit?
I thank every body that trying to help me....
I think we miss understand each other... Let me put it this way...
I have a calculation problem, and I need its result to appear as analog signal, By using a PWM, and changing its duty cycle according to the result..
I need to set up my microcontroller to give me the output...
The datasheet give information about that. And some examples are there, But some of them are not for the C515C and some of them are using the serial communication and so on...
What is the mode, the TCON value, the ET2 , EXEN2 , CRCL values...
i am realy lost... thank you again...
i am realy lost...
for sure you are. I never thought I would say this, but you are reading too much. T2 is an independent, unique timer and what you can find about T0 and T1 (e.g. TCON) has no effect whatsoever on T2.
It seems your device has some unique SFRs but a very simple method could be
void T2ISR(void) interrupt x using y { TF2 = 0; TR2 = 0; if (onflag) { TH2 = OfftimeH; TL2 = OfftimeL; onflag = 0; } else { TH2 = OntimeH; TL2 = OntimeL; onflag = 1; } TR2 = 1: }
Then wherever you get the information re duty cycle you do this
{ TR2 = 0; OfftimeH = xxx; OfftimeL = xxx; OntimeH = xxx; OntimeL = xxx; TR2 = 1;
Erik
You could go to the Infineon website and download DAvE. This tool can guide you in the configuration of T2 and CC1 (I would avoid CC0 if you want to have an automatic reload of T2 on an overflow of T2). DAvE is a GUI that generates your source files and in addition will create a plug in which Keil can directly import and create your project.
So a simple example to generate a PWM on CC1 (P1.1) with T2 assuming that you are running at 10 MHz.
You would configure T2 to work in timer mode and this could and you have only two choices for the prescaler fcpu/6 or fcpu/12. You mentioned in your post you wanted to use compare mode 0 and were looking for a duty cycle of .5 to 1 second. This would require some more work as the time base of the T2 does not support this when the cpu is running at 10 MHz.
You maximum frequency for the period would be 12/10MHz * 65535 = 78.64msec.
If you don't use the CC0 resources then you could use its registers CRCL and CRCH to automatically reload T2 if you wanted a shorter period. You would use mode 0 of T2 for this.
You sated that you wanted to use compare mode 0. In mode 0, upon matching the timer with the compare register contents, the output signal changes from low to high. It goes back to a low level when the timer overflows. As long as compare mode 0 is enabled, the appropriate output pin is controlled by the timer circuit only, and not by the user. Writing to the port will have no effect. The port latch is directly controlled by the two signals timer overflow and compare. The input line from the internal bus and the write-to-latch line are disconnected when compare mode 0 is enabled. Once set up, the output goes on oscillating without any CPU intervention (could be referred to as a set and forget PWM).
For duty cycle control this is generally referred as the amount of time the signal is on (high) during the period. To calculate the DC you could make something that brings you down to simple math (but you also need to "understand" the external hardware configuration, your filter). You could choose a period where the count is 20000 so you could then control the DC by multiplying by 200 for every percent of DC you want.
So if you wanted a 50% DC then 200 * 50 = 10000.
CRCL/CRCH = 65535 - 20000 = 45535 CC1L/CC1H = 45535 + 10000 = 55535
You could use the compare interrupt to change the value of the compare.
Hope this helps.
-Chris
#include "REG515C.H" bit updateDC; unsigned char dcLo; unsigned char dcHi; unsigned int value; void SetDC(unsigned char dc) { value = (200 * (100 - dc)) + 45535; dcLo = (unsigned char) value; dcHi = (unsigned char) (value>> 8); IEX4 = 0; EX4 = 1; } void T2_viIsrCh1(void) interrupt 11 { CCL1 = dcLo; CCH1 = dcHi; EX4 = 0; } void T2_vInit(void) { TL2 = 0xDF; TH2 = 0xB1; T2CON |= 0x91; CRCL = 0xDF; CRCH = 0xB1; CCL1 = 0xEF; CCH1 = 0xD8; CCEN = 0x08; } void main(void) { SYSCON = 0x20; T2_vInit(); WDTREL = 0x80; WDT = 1; SWDT = 1; IRCON = 0; EAL = 1; while(1) { WDT=1; SWDT=1; if (updateDC == 1) { updateDC = 0; SetDC(25); } }; }
Ok.. Thank you Mr. Chris for helping me,, and thank you also for your explanations that helped me a lot,
This is the first time I am using a PWM and timers staff, so please forgive me if I bored you in my basic questions. Ashraf,
After trying PWM program, I added it to my software but I found that it is not working with my program, I am using timer 0 for doing a delay which is used for the LCD. When the execution starts the PWM signal appears, the LCD works for a very short time. And then the execution starts again!! Is it possible making a priority solves the problem, and I tried to change the values of IP0 and IP1, but I did not get any change Is it possible that I am using the wrong bank register?
Please some one look to my software and try with me…
Ashraf, T0 Intialization is:
void T01_vInit(void) { TMOD = 0x31; TL0 = 0x00; TH0 = 0x00; TL1 = 0x00; TH1 = 0x00; ET0 = 1; TCON = 0x00; }
and the delay function is:
unsigned int ctr; void T01_viIsrTmr0(void) interrupt T0INT { T01_vClearTmr(TIMER_0); T01_vLoadTmr(TIMER_0,0xF82C); T01_vStartTmr(TIMER_0); ctr++; TF0 = 0; } void delay(unsigned int delay_msec) { ctr=0; while(ctr<delay_msec); }
In another way, this timer function T0, works fine without adding the PWM program What are the modifications should I change to make them works simultaneously?
please,, any help in my problem..
T01_vClearTmr(TIMER_0); T01_vLoadTmr(TIMER_0,0xF82C); T01_vStartTmr(TIMER_0);
are you 'overmacronizing' or are these function calls? why make a macxro for what you do just once and AVOID LIKE THE PLAGUE function calls from ISRs.
why do you even involve the main for PWM it should be totally handled in an ISR (except stating the width)
why do you not try my suggestion - that was, indeed, help
Mr.Erik, I really appreciate your help, but the problem is that I did not understand what is x,y and how I change the duty cycle during the program...
The given program from Mr.Chris is exactly what I need in a very simple way that I can understand as a beginner.. But it has a problem when I use T0 to have the delay,,,
so, how can I modify one of the two programs to get my needs... (T0 ---> delay function and T2 ---> PWM) and no one interrupts the other...
and thank you all..
I did not understand what is x,y and how I change the duty cycle during the program
OfftimeH = xxx; OfftimeL = xxx; OntimeH = xxx; OntimeL = xxx;
you set Offtime to 0x10000-the number of timer clocks you want the PWM output to be low you set Ontime to 0x10000-the number of timer clocks you want the PWM output to be high, you can change that any time and on the next timer interrupt the new time will be used.
The given program from Mr.Chris is exactly what I need can't help you there, I do not know your derivative, what I have presented is derivative independent and will work on any '51 with a P2.
Good luck,
Running T0 and T2 are independent and you didn't give an example that someone could reproduce to see an issue.
From my point of view I don't understand how you are making the delay using T0. Is it that you want T0 to be a fixed timeout and you want it to be a multiple for your delay function?
Where did you start T0?
What about the watchdog?
Here is a simple example that runs on the C515C WITHOUT issue.
bit updateDC; unsigned char dcLo; unsigned char dcHi; unsigned char dc; unsigned int value; unsigned int ctr; void SetDC(unsigned char dc) { value = (200 * (100 - dc)) + 45535; dcLo = (unsigned char) value; dcHi = (unsigned char) (value>> 8); IEX4 = 0; EX4 = 1; } void T2_viIsrCh1(void) interrupt 11 { CCL1 = dcLo; CCH1 = dcHi; EX4 = 0; } void T01_viIsrTmr0(void) interrupt 1 { TL0 = 0x73; TH0 = 0xF9; ctr++; TF0 = 0; } void delay(unsigned int delay_msec) { ctr=0; /* clear value */ TL0 = 0x73; TH0 = 0xF9; TR0 = 1; /* start T0 */ while(ctr<delay_msec) { WDT=1; SWDT=1; }; TR0 = 0; /* stop T0 */ } void T2_vInit(void) { TL2 = 0xDF; TH2 = 0xB1; T2CON |= 0x91; CRCL = 0xDF; CRCH = 0xB1; CCL1 = 0xEF; CCH1 = 0xD8; CCEN = 0x08; } void T01_vInit(void) { TMOD = 0x01; ET0 = 1; /* Timer 0 interrupt enabled */ TCON = 0x00; } void main(void) { SYSCON = 0x20; T2_vInit(); T01_vInit(); WDTREL = 0x80; WDT = 1; SWDT = 1; dc = 75; /* set DC to 75% */ IRCON = 0; EAL = 1; while(1) { delay(100); /* delay ~100 msec */ P1 ^= 0x04; /* toggle P1.2 */ if (updateDC == 1) { updateDC = 0; SetDC(dc); } }; }
Hello,
A schematic would not be enough, you also need to be able to program a chip. I have something similar (I made it myself), I use a microprocessor. Are you familiar with Paratype C51Basic ?
I tried Mr.Chris's program.. and the problem still there, If I made the program as:
while(1) { delay(100); /* delay ~100 msec */ P1 ^= 0x04; /* toggle P1.2 */ }
It will make a delay and toggle P1.2
And if I made the program as:
updateDC == 1 dc = 75; while(1) { if (updateDC == 1) { updateDC = 0; SetDC(dc); } };
The result will be a PWM with a duty cycle of 75%
But when I put the program as:
while(1) { delay(100); /* delay ~100 msec */ P1 ^= 0x04; /* toggle P1.2 */ if (updateDC == 1) { updateDC = 0; SetDC(dc); } };
!!!!
The pin will toggle and no PWM signal !! What is the problem?! please help
you present us with a ton of unknowns such as SetDC(dc); which nobody (but you) has any idea of what does.
If you want ANY precision in your PWM get out of main() use the T2 interrupt ISR.
You are fooling around with delays in main() written in C that a) may change any day (there is no restriction on how C is implemented and it often changes from release to release) and b) 'hangs' your processor and c) if e.g. an UART interrupt happes will change.
go cure your interruptifobia and then try again.
The pin will toggle and no PWM signal !! if the pin toggles you have a PWM signal, maybe not the frequency or duty cycle you want, but you DO have a PWM signal.
This is dead simple if you do not try to be fancy here is some pseudocode
in main() where you find out what the PWM is to be you set two variables: Up and Down, Up being the number of T2 clocks you want the PWM output to be high and Down being the number of T2 clocks you want the PWM output to be low. For a contstant frequency Up plus Down must always be the same.
Then you have the variables High and Low High being (10000h minus Up) + fiddle and Low being (10000h minus Down) + fiddle. You also need a bit variable, let's call it On
then in the T2 ISR stop the timer if ON -- clear On -- set the timer value (TH and TL) to the value Low -- clear the port pin else -- set On -- set the timer value (TH and TL) to the value High -- set the port pin endif start the timer exit
fiddle above is the number of T2 clocks that would occur during ISR execution which you can calculate with "the bible" chapter 2. If you need ultimate precision code the T2 ISR in assembler so the time it takes will be constant.