Introduction to MSP430G2553 and MSP430F5336 timers, capture comparators and interrupts[Copy link]
Before studying the timer and capture comparator, let's discuss what they can do. 1. Timing, generating a fixed frequency waveform, or making the LED flash at a fixed time 2. Generate Timer0 timing interrupt, perform certain functions at a certain time interval, such as the use of ultrasonic sensors, you can set the measurement frequency of ultrasonic distance measurement 3. Can measure the high and low level time or frequency of pulses or PWM waves 4. Pulse counting 5. Capture comparison mode can generate PWM waves 6. Can generate PPM waves 7. Can measure the duty cycle of each channel of PPM waves So the timer is a high-frequency resource in the microcontroller. Don't use the timer's IO port directly for input and output. This is a bit wasteful. First, we analyze MSP430G2553. Through the user manual and datasheet, we know that G2553 only has timer A, no timer B, and no timer A2. Timer A only has capture comparator 0 (TA0.CCI0A pin P1.1), capture comparator 1 (TA0.CCI1A pin P1.2) and other resources. Timer_A is a 16-bit timer, which means that it can count up to 65536. When the timing reaches the practice or the capture comparison condition is met, the timer A interrupt can be triggered. Here is a certain introduction to interrupts. Interrupts pause the running program of the CPU and turn to execute the corresponding interrupt service program. After the interrupt is completed, the interrupted program returns to the interrupted program and continues to run. The existence of interrupts is very necessary, which can handle emergencies well and does not conflict with the main program content. These explanations may not be easy to understand. I will now give an example to illustrate the necessity of interrupts. For example, I want to use the MSP430 series of microcontrollers to control the quadcopter. First, it needs to continuously generate four 200HZ PWM waves, and also needs to continuously read the acceleration and angular velocity data of the flying message device transmitted from the MPU6050. It also needs to obtain the distance information returned by the ultrasonic wave to avoid obstacles, and also needs to perform four-element fusion to calculate the Euler angle and perform PID iteration. So many operations need to be performed simultaneously. If there is no interruption, we may use delay to generate a 200HZ PWM wave, that is, write some delay programs to delay to 5ms and then negate it to generate a 200HZ pwm wave. However, your program needs to continuously execute the delay program and cannot be interrupted, because once it is interrupted, the cycle of the waveform you generate will change, so we will not be able to add sensors such as MPU6050. However, using interrupts can solve this problem well. Our main program continuously executes the PID iteration operation. After the 5ms timing cycle is reached, the timer interrupt is entered and a pwm wave is generated. After the serial port interrupt is reached, the serial port is entered to read the data of MPU6050. After the high level of the echo signal is reached, the timer corresponding to the ultrasonic wave is entered to read the high level. After these interrupts are completed, the PID iteration is continued in the main function. In this way, the information can be continuously updated and the waveform can be generated without affecting the execution of the main function. Therefore, it is very important to learn how to use interrupts. The sources of interrupts include internal interrupts and external interrupts, and the priority can be set. To be able to enter the interrupt, you must first set the interrupt enable in the relevant registers. You can refer to the code and some materials to learn these contents by yourself. Now I will talk about some typical examples of timer interrupts: [plain] view plain copy #include void main(void) { WDTCTL = WDTPW + WDTHOLD; // Turn off the watchdog timer P1DIR |= 0x01; // Set P1.0 as output CCTL0 = CCIE; // TA0CCR0 timer enable, the CCTL0 here is actually TACCTL0 in the macro definition CCR0 = 1000-1; // Set the count to 1000 TACTL = TASSEL_1 + MC_1; // Use ACLK=32768HZ up-counting mode_BIS_SR(LPM3_bits + GIE); // Enter LPM3 interrupt and enable interrupt } // Timer A0 capture comparator 0 interrupt #pragma vector = TIMER0_A0_VECTOR __interrupt void Timer_A (void) { P1OUT ^= 0x01; // P1.0 port is inverted to generate a square wave} /* * The period of the square wave is: 32768hz/1000*2=16HZ */ The above example uses the interrupt of capture comparator A0. A0 is different from other capture comparator interrupts. The interrupt format of A0 is shown in this program. We will talk about other capture comparator interrupts later. The above program is basically annotated and easy to understand. What we need to pay attention to is the four modes of timer counting, namely stop mode (no counting), up counting mode (counting from 0 to TAxCCR0), continuous counting mode (counting from 0 to 0FFFFh), up-down counting mode (counting from 0 to TAxCCR0 and then down-counting to 0 repeatedly), so the counting period can be changed by changing TAxCCR0 through up-counting or up-down counting mode, that is, changing the waveform period to be generated. In addition, there are input and output mode definitions, which can generate waveforms of different requirements, such as pwm waves, etc. You can refer to the user manual for details. Let's look at the second example: [plain] view plain copy #include int main(void) { WDTCTL = WDTPW | WDTHOLD; // Turn off the watchdog timer P1DIR|=0X01; TACTL=TASSEL_2+MC_2+TAIE; //SMCLK=1.048576Mhz timer is 16 bits, overflow count is 65536 _BIS_SR(LPM0_bits+GIE); //f=1.048576MHZ/65536*2=8hz } #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer_A(void) { switch(TA0IV) { case 2:break; case 4:break; case 10:P1OUT^=0x01; break; } } /* * Timer A has two different interrupt vector addresses, one is the timer overflow interrupt of CCR0, which enters the interrupt after the timing or counting cycle time is up. * The program is as follows: * #pragma vector=TIMER0_A0_VECTOR * __interrupt void Timer_A0(void) * { * } * The other is CCR1/CCR2 and TAR count overflow interrupt* The program is shown in this program* Case 2 is the interrupt generated by CCR1, case 4 is the interrupt generated by CCR2, and case 10 is the timer TAR overflow interrupt*/ This example uses the interrupt of A1. As mentioned in the comments, case 2 is the interrupt generated by CCR1, case 4 is the falling edge interrupt generated by CCR2, and case 10 is the timer TAR overflow interrupt. So if you want to invert P1.0 at 8hz, write this sentence in case 10. Of course, you can also set it to be inverted when the counting time of CCR1 or CCR2 is up, as shown in the following code: [plain] view plain copy #include int main(void) { WDTCTL = WDTPW +WDTHOLD; // Stop watchdog timer P1DIR|=0x01; CCTL1=CCIE; CCR1=50000; TACTL=TASSEL_2+MC_2; //SMCLK=1.048576MHZ continuous counting mode counts 100000, so the frequency is about 10hz_BIS_SR(LPM0_bits+GIE); } #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer_A(void) { switch(TA0IV) { case 2: { P1OUT^=0x01; //Capture comparator 1 triggers CCR1+=50000; } break; case 4:break; case 10:break; } } /* * f=10.89hz * Use capture comparator 1 */ [plain] view plain copy #include void main(void) { WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer P1SEL|=BIT1+BIT2; P1DIR|=BIT0+BIT1+BIT2; CCTL0=OUTMOD_4+CCIE; CCTL1=OUTMOD_4+CCIE; TACTL=TASSEL_2+MC_2+TAIE; _BIS_SR(LPM0_bits+GIE); } #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A0(void) { CCR0+=200; } #pragma vector=TIMER0_A1_VECTOR __interrupt void Timer_A1(void) { switch(TA0IV) { case 2:CCR1+=1000; break; case 10:P1OUT^=0x01; break; } } /* * For P1.0 f=1M/65536*2=8HZ * For P1.1, i.e. CCR0 f=1M/2*200=2500hz * For P1.2, i.e. CCR1 f=1M/2*1000=500HZ */ The above code is roughly an introduction to timer interrupts. I believe everyone can understand it easily. However, there will be a question here. If no interrupt is used, can a signal with a certain period be generated? The answer is yes. The CCR1 and CCR0 pins can be configured as comparison output mode to generate a square wave of a certain frequency. This is relatively simple, I will not explain it in detail, the specific code and comments are as follows: [plain] view plain copy #include void main(void) { WDTCTL = WDTPW + WDTHOLD; // Turn off the watchdog timer P1DIR |= 0x02; // P1.1 output P1SEL |= 0x02; // P1.1 second function selection CCTL0 = OUTMOD_4; // CCR0 compare output mode 4 CCR0 = 500-1; // The count is 500, so the period is SMCLK/1000 TACTL = TASSEL_2 + MC_1; // SMCLK is the clock, up mode _BIS_SR(CPUOFF); // Turn off the CPU and enter sleep, the P1.1 frequency is SMCLK/1000 } The following example is an example of generating a PWM wave waveform without interruption. I will write an article later to explain the example of generating a PWM wave with interruption. [plain] view plain copy #include void main(void) { WDTCTL = WDTPW + WDTHOLD; // Dog off P1DIR |= 0x0C; // P1.2 P1.3 set to output P1SEL |= 0x0C; // P1.2 P1.3 Select TA1/2 function CCR0 = 512-1; // Set PWM wave period CCTL1 = OUTMOD_7; // CCR1 mode 7 CCR1 = 384; // Set CCR1 duty cycle 75% TACTL = TASSEL_2 + MC_1; // SMCLK is the clock, up count mode _BIS_SR(CPUOFF); // Enter sleep} The above is basically the description of Timer A of MSP430G2553. There are some other routines that may configure the clock to ACLK, or generate different duty cycles, or use different pins, but the principles are similar. What everyone should pay attention to is which clock and which counting mode to choose, so that timer A can be used well. Let's also look at the use of timers in MSP430F5336. The timer resources of F5336 are relatively rich. The timer has four different timer modules: Timer A0, Timer A1, Timer A2, and Timer B. Timer A0 has five capture comparators, CCI0-CCI4, and CCI1B and CCI2B are available, which are the second functions of managing P1.1-P1.7; Timer A1 and Timer A2 both have three capture comparators, CCI0-CCI2, on pin P3; Timer B has seven capture comparators, CCI0-CCI6, on pin P4; the specific principle is similar to that of G2553, but the registers are slightly different. I will not explain in detail, and will just paste the code: Timer A: [plain] view plain copy #include /* * The register situation of Timer A is basically the same as that of G2553, and you can refer to Chinese materials* The frequency of P1 is 10hz, so an interrupt occurs once every 48ms* SMCLK is about 1M, so 50000us is equivalent to 50ms */ void main(void) { WDTCTL = WDTPW + WDTHOLD; // Turn off watchdog P1DIR |= 0x01; // Set P1.0 to output TA0CCTL0 = CCIE; // Enable CCR0 interrupt TA0CCR0 = 50000; TA0CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, up-counting mode, clear TAR counter __bis_SR_register(LPM0_bits + GIE); // Enter LPM0, enable interrupt } // TA0 interrupt service routine #pragma vector=TIMER0_A0_VECTOR __interrupt void TIMER0_A0_ISR(void) { P1OUT ^= 0x01; // Invert the output state of P1.0 port } [plain] view plain copy #include /* * The register situation of timer A is basically the same as that of G2553, you can refer to the Chinese information* 65536 overflow once* SMCLK is about 1M, so 65535us is equivalent to 65ms */ void main(void) { WDTCTL = WDTPW + WDTHOLD; // Turn off the watchdog P1DIR |= 0x01; // Set P1.0 to output TAA1CTL = TASSEL_2 + MC_2 + TACLR + TAIE; // SMCLK, continuous counting mode, clear TAR, and enable TAIFG interrupt __bis_SR_register(LPM0_bits + GIE); // Enter LPM0 and start interrupt } // TA1 interrupt service routine #pragma vector=TIMER1_A1_VECTOR __interrupt void TIMER1_A1_ISR(void) { switch(__even_in_range(TA1IV,14)) { case 0: break; // No interrupt case 2: break; // TA1CCR1 CCIFG interrupt case 4: break; // TA1CCR2 CCIFG interrupt case 6: break; // TA1CCR3 CCIFG interrupt case 8: break; // TA1CCR4 CCIFG interrupt case 10: break; // TA1CCR5 CCIFG interrupt case 12: break; // TA1CCR6 CCIFG interrupt case 14: P1OUT ^= 0x01; // TAIFG interrupt break; default: break; } } [plain] view plain copy #include /* * Make P1.2 (TA0CCR1) and P1.3 (TA0CCR2) and P1.4 (TA0CCR3) and P1.5 (TA0CCR4) output waveforms with duty cycles of 20 and %40 and %60 and %80 respectively * The frequency is 980000/TA0CCR0=400hz * It can be used to control the motor*/ void main(void) { WDTCTL = WDTPW + WDTHOLD; // Turn off the watchdog P1DIR |= BIT2+BIT3+BIT4+BIT5; // Set P1.2 and P1.3 as outputs P1SEL |= BIT2+BIT3+BIT4+BIT5; // P1.2 and P1.3 pin function is selected as timer output TA0CCR0 = 2500; // PWM period definition TA0CCTL1 = OUTMOD_7; // CCR1 comparison output mode 7: reset/set TA0CCR1 = 500; // CCR1 PWM duty cycle definition TA0CCTL2 = OUTMOD_7; // CCR2 comparison output mode 7: reset/set TA0CCR2 = 1000; // CCR2 PWM duty cycle definition TA0CCTL3 = OUTMOD_7; // CCR1 comparison output mode 7: reset/set TA0CCR3 = 1500; // CCR1 PWM duty cycle definition TA0CCTL4 = OUTMOD_7; // CCR2 comparison output mode 7: reset/set TA0CCR4 = 2000; // CCR2 PWM duty cycle definition TA0CTL = TASSEL_2 + MC_1 + TACLR; // ACLK, up-counting mode, clear TAR counter __bis_SR_register(LPM3_bits); // Enter LPM3 } Timers A1, A2, and B are similar and will not be repeated. The above is basically the principle and usage of the timer.