We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi everybody,
I'm a beginner trying to generate a PWM wave on a C8051F330 micro controller using C programming language. I have many doubts regarding this exercise. It would be really helpful if someone clarifies my doubtswhich will eventually get me going.
Can anybody please explain me roughly the flow of the following program...??
// Include Files // // Global constants // // Function Prototypes // // Main Routine //
void main (void) { PCA0MD &= ~0x40; Oscillator_Init (); Port_Init (); External_Interrupt_Init (); Timer2_Init (); PCA_Init (); EA = 1; while (1); }
// Oscillator Initialisation // void Oscillator_Init (void) { //do something// }
//Port Initialisation // void Port_Init (void) { //do something// }
//External Interrupt Initialisation // void External_Interupt_Init (void) { //do something// EX0 = 1; }
//Timer2 Initialisation // void Timer2_Init (void) { TMR2CN=0x00; TMR2RL= RELOAD_VALUE; TMR2 = 0xFFFF; // Set to reload immediately // }
//PCA Initialisation // void PCA_Init (void) { //do something// }
//Interrupt Service Routine (ISR) // INTERRUPT(INT0_ISR, INTERRUPT_INT0) { TR2 = 1; }
This program is in C. I'm here trying to understand the program flow. I know that the "main()" is the first function that gets executed here. And then the line "PCA0MD &= ~0x40" followed by Osillator_Init function. The program control actually goes into the called Oscillator_Init function (below main()) and executes it,.and so on...
In case of External_Interrupt_Init(),I have enabled /INT0 interrupt using "EX0=1". Where does the program flow control go from here...??
And in ISR where does the program control go after executing TR2 = 1,.
Please folks help me find answers to my doubts.
So, I see that your teacher have given you a skeleton code for your programming exercise.
Well, I'm pretty sure that your teacher also is of the belief that students who have been visiting the lessons would also have enough skill to know how to solve this programming exercise. And you have probably also received documentation or links to documentation.
So exactly where are you stuck? What part of the microcontroller course did you understand, and what part did you fail to understand?
Have you spent time with the documentation for the processor?
Have you checked for application notes?
Have you checked what potentially useful examples there are on the manufacturers home page or installed with the Keil tools? Or available on this site?
No, you have completely got me wrong. This is completely my code. It's just that I haven't pasted the whole code. No, teacher has given the code to me. I'm not in school or college level doing this. I'm an engineer who has just started to familiarise myself with programming in Keil IDE at my job. This is my first programming attempt with Keil. I did go through the documentation for the processor C8051F330 and the necessary application notes. I have gone through the basic examples of External Interrupts, Timers and Blinky which I had access to. My doubts are clearly stated in my first post. But again for your reference:: "I'm here trying to understand the program flow. I know that the "main()" is the first function that gets executed here. And then the line "PCA0MD &= ~0x40" followed by Osillator_Init function. The program control actually goes into the called Oscillator_Init function (below main()) and executes it,.and so on... What are Initialisation functions ? Why do they have to be coded one after the other as functions..?? Can I not have some functional code inside the Initialisation function..???
In case of External_Interrupt_Init(),I have enabled /INT0 interrupt using "EX0=1". Where does the program flow control go from here...?? According to my understanding the program control jumps to service the ISR (Interrupt service routine).
And in ISR where does the program control go after executing TR2 = 1 (code to start Timer2),."
As you already said this function does nothing but to set EX0=1, then it move to next line of the main function.
Do some basic search on google about what is an "Interrupt service routine".
Thanks for your reply. Yeah I understand that, as I have EX0 = 1 (External Interrupt for /INT0 set) the program flow jumps to servise the ISR(Interrupt service routine). But in the ISR I have set the Timer2 to logic 1 which means Timer2 is set to run. Could you please tell me Where does the program control go after this..??
Again, after the execution of the ISR the control should go back to service the instruction that would have been executed if the Interrupt request had not occured. But in this case, as I start a timer, where could the program control go....??
Thanks very much for your reply:)
I'm not familiar with processor you are using or how to use the registers to set the timers for that processor.
My guess is that in your program the timer event invokes the ISR, after the ISR is served the control carries on in the while(1) loop waiting for the next timer event that will invoke ISR and so on.
Thanks Christian,. The Timer I use here is Timer2 in its 16 bit auto reload state. I have coded in such a way that when Timer overflows I put a new value in the PCA,..But in ISR when I start Timer2 which should eventually causes Timer overflow and for this I want to understand where does the program control go after I start Timer 2 in ISR..?? This is my doubt:(
The questions you're asking are about pretty basic embedded programming concepts - they have nothing specifically to do with Keil, nor even the C8051F330 microcontroller.
The Keil manuals and microcontroller datasheet, etc, do assume that you are proficient.
Therefore, especially as this is for your job, I most strongly recommend that you take an embedded training course.
There are some (providers) mentioned here: http://www.keil.com/events/ (see "Third party training")
And here: blog.antronics.co.uk/.../08
You local SiLabs and/or Keil distributors should be able to suggest others - and may even provide training themselves...
Well - the only reason to have individual initialization functions - and to have just initialization in them - is basically a question of maintenance.
Having many small initialization functions makes it easy to enable/disable individual peripherials when trying to debug specific issues.
And separate initialization code from action code means that all the initialization can be run first, before the program enters the main loop. Then the action code can do its thing based on whatever stimuli the program sees - keypresses, timers interrupting, ...
Interrupts aren't magical. There are some minor differences between processor architectures. But you normally have nested interrupts or not. With nested interrupts, a higher-prio interrupt source may activate while the processor is already servicing a lower-prio interrupt source. Interrupt nesting is like a stack. When the high-prio interrupt ends, the processor drops back to the less prioritized interrupt and continues with the next instruction. When that interrupt handler, the program returns to the main loop code.
If the processor doesn't support interrupt nesting, or the code doesn't enable interrupt nesting, then the first interrupt service routine (whatever prio it may have) must end, before a new interrupt service routine can get activated. Often, the processor manages to do maybe one instruction from the main loop before activating next pending interrupt service handler.
Enabling interrupts doesn't mean an interrupt will happen. It only means that interrupts are globally allowed, or that interrupts from a specific peripherial is specifically allowed. There still needs to be an event of some kind for that peripherial to actually rise the interrupt service flag asking for an interrupt to happen. Such as the UART receiving a character. Sometimes the interrupt comes directly - like enabling UART transmit interrupts and instantly get a "I'm hungry - feed me" transmit interrupt.
Anyway - the route to learn a new processor is to test one peripherial at a time. And only combine multiple peripherials as the individual modules have been tested. Easy to do when there is a separate function to initialize. A separate interrupt service handler. Individual "action" functions for interacting - like checking if data have been received from the UART or adding outgoing data to a send queue.
While learning a new peripherial, it's also possible to test much of the code in polling mode directly from the main loop before upgrading the code to use interrupt handlers, separating the timeline of the peripherial device from the timeline of the main program flow.
Anyway - in the end it's impossible to help people except when they are stuck with explicit and well-defined problems.
The program don't care about you starting a timer. The program flow will just continue while the timer does what it should do in the background.
It isn't until the timer reaches specific states (like counter overflow or whatever the specific timer supports and have been configured for) that the timer hardware may try to request an interrupt.
So it doesn't matter much if you start ticking the timer while in main loop or inside an interrupt service routine. The action only happens when the timer have done the ticking and wants to go BOOOM :)
Thank you very much for your reply. It was indeed helpful. As I told you before I'm trying to generate PWM signal on C8051F330 micro controller using External Interrupts and Timers. I want the External Interrupt to start a Timer. When the Timer overflows I want to put in a new value in the PCA register for PWM. I have developed the following code. I'm stuck with the code in the ISR. I use a 16 bit Timer 2 with auto-reload capability. I'm monitoring the Timer2 overflow flag in the ISR which is not correct,. Can you please suggest me what has to be done in this case? //----------------------------------------------------------------------------- // Include Files //-----------------------------------------------------------------------------
#include <compiler_defs.h> // Macro definitions for 8051 Compiler // #include <C8051F336_defs.h> // SFR declarations //
//----------------------------------------------------------------------------- // Global Constants //-----------------------------------------------------------------------------
#define SYSCLK 24500000/12 // Clock speed in Hz //
#define TIMER_TICKS_PER_MILLI_SECOND SYSCLK/100/ 1000
#define RELOAD_VALUE -TIMER_TICKS_PER_MILLI_SECOND
SBIT (SW2, SFR_P0, 7); // SW1==0 means switch depressed
//----------------------------------------------------------------------------- // Function Prototypes //-----------------------------------------------------------------------------
void Oscillator_Init (void); // Configure the system clock // void Port_Init (void); // Configure the Crossbar and GPIO // void Ext_Interrupt_Init (void); // Configure External Interrupts (INT0) // void Timer2_Init (int counts); // Configure Timer2 // void PCA_Init (void); // Configure PCA for PWM mode //
INTERRUPT_PROTO (INT0_ISR, INTERRUPT_INT0);
//----------------------------------------------------------------------------- // MAIN Routine //----------------------------------------------------------------------------- void main (void) { PCA0MD &= ~0x40; // Disable Watchdog timer //
Oscillator_Init(); // Initialize the system clock // Port_Init (); // Initialize crossbar and GPIO // Ext_Interrupt_Init(); // Initialize External Interrupts // Timer2_Init(SYSCLK/100000); // Initialize Timer2 to generate interrupts at a 20 Hz rate// PCA_Init (); // Initialize PCA for PWM //
EA = 1;
while(1); // Infinite while loop waiting for an interrupt // }
//----------------------------------------------------------------------------- // Oscillator_Init //----------------------------------------------------------------------------- // This routine initializes the system clock to use the precision internal // oscillator as its clock source. //----------------------------------------------------------------------------- void Oscillator_Init (void) { CLKSEL = 0x00; // Clock Select. SYSCLK is derived from the Internal High Frequency Oscillator and scaled per IFCN bits in register OSCICN // OSCICN = 0x83; // Internal H-F Oscillator enabled and SYSCLK derived from Internal H-F Oscillator divided by 1 // }
//----------------------------------------------------------------------------- // Port_Init //-----------------------------------------------------------------------------
void Port_Init (void) { XBR1 = 0x40; // Enable crossbar and weak pull ups // P0SKIP = 0x7F; // Skip unused pins on P0 and P1 // P1SKIP = 0xFF;
}
//----------------------------------------------------------------------------- // Ext_Interrupt_Init //-----------------------------------------------------------------------------
void Ext_Interrupt_Init (void) { TCON = 0x01; // INT 0 is edge triggered // IT01CF = 0x07; // INT0 active low; INT0 on P0.7 // EX0 = 1; // Enable INT0 interrupts // }
//----------------------------------------------------------------------------- // Timer2_Init //-----------------------------------------------------------------------------
void Timer2_Init (int counts) { TMR2CN = 0x00; // Stop Timer2. Clear T2SPLIT. // Use SYSCLK/12 as timebase, 16-bit auto-reload // CKCON &= 0x30; // Timer2 High and Low byte uses the SYSCLK // TMR2RL = -counts; // Initialize reload value TMR2 = 0xFFFF; // Set to reload immediately
//----------------------------------------------------------------------------- // PCA_Init //-----------------------------------------------------------------------------
void PCA_Init(void) {
PCA0MD = 0x00; PCA0CPM0 = 0xC7; // PWM 16, ECOM, TOG, PWM, ECCF bits set //
//----------------------------------------------------------------------------- // Interrupt Service Routines //-----------------------------------------------------------------------------
INTERRUPT(INT0_ISR, INTERRUPT_INT0) {
TR2 = 1; // Start Timer2 // while (TF2H ==1) { for ( RELOAD_VALUE = 0; RELOAD_VALUE <65535; RELOAD_VALUE++ ) { PCA0CPH0 = RELOAD_VALUE; } continue; }
If you check just above the message input box, there is information how to post source code. That would make the post look like:
if (condition) { fix_problem(); }
Note that you should use two interrupt handlers. One to catch the external interrupt, i.e. to trig the start of your PWM generation. Then a second interrupt handler to catch the timer events.
So your main loop can do whatever it likes. Possibly compute new reload values for the PWM output, in case you want to generate a sine wave or something else.
The only question is - if the external interrupt starts the PWM generation, it will continue forever unless you also add a mechanic to stop it. Maybe let every second external interrupt start or stop PWM output. But that requires that you make sure the signal is debounced so you don't get lots of on/off interrupts from just a single button press.
Thanks again for the reply. Okay I will make sure I post source code in that format from next time. Sorry for this time. Will an ISR for Timer 2 get executed without Enabling Timer 2 Interrupt (IT2 = 1;)...?? I know I'm monitoring its Interrupt pending flag (TF2H) but...?
You need to enable any interrupt if you want the processor to be allowed to generate any interrupts for that interrupt source.
If no interrupts are enabled, then all you can do is spend time polling. But that isn't as fun. And generating PWM output means that you want to constantly figure out what next value to use so you can generate a the wanted curve shape. Unless your goal is just to just have a fixed value to generate a DC voltage (after proper filtering of the produced pulse train output).