Contiki 2.6
|
00001 /* 00002 * Copyright (c) 2010, Mariano Alvira <mar@devl.org> and other contributors 00003 * to the MC1322x project (http://mc1322x.devl.org) 00004 * All rights reserved. 00005 * 00006 * Redistribution and use in source and binary forms, with or without 00007 * modification, are permitted provided that the following conditions 00008 * are met: 00009 * 1. Redistributions of source code must retain the above copyright 00010 * notice, this list of conditions and the following disclaimer. 00011 * 2. Redistributions in binary form must reproduce the above copyright 00012 * notice, this list of conditions and the following disclaimer in the 00013 * documentation and/or other materials provided with the distribution. 00014 * 3. Neither the name of the Institute nor the names of its contributors 00015 * may be used to endorse or promote products derived from this software 00016 * without specific prior written permission. 00017 * 00018 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 00019 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00021 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 00022 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 00023 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 00024 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00025 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00026 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 00027 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 00028 * SUCH DAMAGE. 00029 * 00030 * This file is part of libmc1322x: see http://mc1322x.devl.org 00031 * for details. 00032 * 00033 * 00034 */ 00035 00036 #include <mc1322x.h> 00037 #include <stdlib.h> 00038 #include "pwm.h" 00039 00040 static struct { 00041 uint32_t period; 00042 uint32_t guard; 00043 uint32_t pad_forced; 00044 } pwm_info[4]; 00045 00046 static inline void pad_set_output(int timer_num) { // set to output (when in GPIO mode) 00047 switch (timer_num) { 00048 case 0: GPIO->DATA_SEL.TMR0_PIN = 1; GPIO->PAD_DIR.TMR0_PIN = 1; break; 00049 case 1: GPIO->DATA_SEL.TMR1_PIN = 1; GPIO->PAD_DIR.TMR1_PIN = 1; break; 00050 case 2: GPIO->DATA_SEL.TMR2_PIN = 1; GPIO->PAD_DIR.TMR2_PIN = 1; break; 00051 case 3: GPIO->DATA_SEL.TMR3_PIN = 1; GPIO->PAD_DIR.TMR3_PIN = 1; break; 00052 default: break; 00053 } 00054 } 00055 00056 static inline void pad_set_zero(int timer_num) { // set to zero in GPIO mode 00057 switch (timer_num) { 00058 case 0: GPIO->DATA_RESET.TMR0_PIN = 1; GPIO->FUNC_SEL.TMR0_PIN = 0; break; 00059 case 1: GPIO->DATA_RESET.TMR1_PIN = 1; GPIO->FUNC_SEL.TMR1_PIN = 0; break; 00060 case 2: GPIO->DATA_RESET.TMR2_PIN = 1; GPIO->FUNC_SEL.TMR2_PIN = 0; break; 00061 case 3: GPIO->DATA_RESET.TMR3_PIN = 1; GPIO->FUNC_SEL.TMR3_PIN = 0; break; 00062 default: break; 00063 } 00064 } 00065 00066 static inline void pad_set_one(int timer_num) { // set to one in GPIO mode 00067 switch (timer_num) { 00068 case 0: GPIO->DATA_SET.TMR0_PIN = 1; GPIO->FUNC_SEL.TMR0_PIN = 0; break; 00069 case 1: GPIO->DATA_SET.TMR1_PIN = 1; GPIO->FUNC_SEL.TMR1_PIN = 0; break; 00070 case 2: GPIO->DATA_SET.TMR2_PIN = 1; GPIO->FUNC_SEL.TMR2_PIN = 0; break; 00071 case 3: GPIO->DATA_SET.TMR3_PIN = 1; GPIO->FUNC_SEL.TMR3_PIN = 0; break; 00072 default: break; 00073 } 00074 } 00075 00076 static inline void pad_set_normal(int timer_num) { // set to TMR OFLAG output 00077 switch (timer_num) { 00078 case 0: GPIO->FUNC_SEL.TMR0_PIN = 1; break; 00079 case 1: GPIO->FUNC_SEL.TMR1_PIN = 1; break; 00080 case 2: GPIO->FUNC_SEL.TMR2_PIN = 1; break; 00081 case 3: GPIO->FUNC_SEL.TMR3_PIN = 1; break; 00082 default: break; 00083 } 00084 } 00085 00086 /* Initialize PWM output. 00087 timer_num = 0, 1, 2, 3 00088 rate = desired rate in Hz, 00089 duty = desired duty cycle. 0=always off, 65536=always on. 00090 enable_timer = whether to actually run the timer, versus just configuring it 00091 Returns actual PWM rate. */ 00092 uint32_t pwm_init_ex(int timer_num, uint32_t rate, uint32_t duty, int enable_timer) 00093 { 00094 uint32_t actual_rate; 00095 volatile struct TMR_struct *timer = TMR_ADDR(timer_num); 00096 int log_divisor = 0; 00097 uint32_t period, guard; 00098 00099 /* Turn timer off */ 00100 TMR0->ENBL &= ~(1 << timer_num); 00101 00102 /* Calculate optimal rate */ 00103 for (log_divisor = 0; log_divisor < 8; log_divisor++) 00104 { 00105 int denom = (rate * (1 << log_divisor)); 00106 period = (REF_OSC + denom/2) / denom; 00107 if (period <= 65535) 00108 break; 00109 } 00110 if (log_divisor >= 8) 00111 { 00112 period = 65535; 00113 log_divisor = 7; 00114 } 00115 00116 /* Guard value (for safely changing duty cycle) should be 00117 about 32 CPU clocks. Calculate how many timer counts that 00118 is, based on prescaler */ 00119 guard = 32 >> log_divisor; 00120 if (guard < 2) guard = 2; 00121 00122 /* Period should be about 50% longer than guard */ 00123 if (period < ((guard * 3) / 2)) 00124 period = guard + 4; 00125 00126 /* Store period, guard, actual rate */ 00127 pwm_info[timer_num].period = period; 00128 pwm_info[timer_num].guard = guard; 00129 actual_rate = REF_OSC / (period * (1 << log_divisor)); 00130 00131 /* Set up timer */ 00132 pwm_duty_ex(timer_num, duty); // sets CMPLD1, LOAD 00133 timer->SCTRLbits = (struct TMR_SCTRL) { 00134 .OEN = 1, // drive OFLAG 00135 }; 00136 timer->CSCTRLbits = (struct TMR_CSCTRL) { 00137 .CL1 = 0x01, // Reload COMP1 when COMP1 matches 00138 }; 00139 timer->COMP1 = timer->CMPLD1; 00140 timer->CNTR = timer->LOAD; 00141 timer->CTRLbits = (struct TMR_CTRL) { 00142 .COUNT_MODE = 1, // Count rising edge of primary source 00143 .PRIMARY_CNT_SOURCE = 8 + log_divisor, // Peripheral clock divided by (divisor) 00144 .LENGTH = 1, // At compare, reset to LOAD 00145 .OUTPUT_MODE = 6, // Set on COMP1, clear on rollover 00146 }; 00147 00148 pad_set_output(timer_num); 00149 pad_set_normal(timer_num); 00150 00151 if (enable_timer) { 00152 TMR0->ENBL |= (1 << timer_num); 00153 } 00154 00155 // printf("pwm timer %d, addr %p, requested rate %d, actual rate: %d, period %d, guard %d, divisor %d\r\n", 00156 // timer_num, timer, rate, actual_rate, period, guard, 1 << log_divisor); 00157 00158 return actual_rate; 00159 } 00160 00161 /* Change duty cycle. Safe to call at any time. 00162 timer_num = 0, 1, 2, 3 00163 duty = desired duty cycle. 0=always off, 65536=always on. 00164 */ 00165 void pwm_duty_ex(int timer_num, uint32_t duty) 00166 { 00167 uint16_t comp1, load; 00168 volatile struct TMR_struct *timer = TMR_ADDR(timer_num); 00169 uint32_t period = pwm_info[timer_num].period; 00170 00171 duty = (duty * period + 32767) / 65536; 00172 00173 /* We don't use the "variable PWM" mode described in the datasheet because 00174 there's no way to reliably change the duty cycle without potentially 00175 changing the period for one cycle, which will cause phase drifts. 00176 00177 Instead, we use the "Set on compare, clear on rollover" output mode: 00178 00179 waveform: |_________| |----------| 00180 counter: 0 COMP1 LOAD 65535 00181 00182 The low portion of the wave is COMP1 cycles long. The 00183 compare changes the counter to LOAD, and so the high 00184 portion is (65536 - LOAD) cycles long. 00185 00186 Now, we just have to make sure we're not about to hit COMP1 00187 before we change LOAD and COMPLD1. If (COMP1 - CNTR) is less 00188 than GUARD cycles, we wait for it to reload before changing. 00189 */ 00190 00191 if (duty == 0) { 00192 pad_set_zero(timer_num); 00193 pwm_info[timer_num].pad_forced = 1; 00194 return; 00195 } 00196 00197 if (duty >= period) { 00198 pad_set_one(timer_num); 00199 pwm_info[timer_num].pad_forced = 1; 00200 return; 00201 } 00202 00203 if (pwm_info[timer_num].pad_forced) { 00204 pad_set_normal(timer_num); 00205 pwm_info[timer_num].pad_forced = 0; 00206 } 00207 00208 comp1 = (period - duty) - 1; 00209 load = (65536 - duty); 00210 00211 /* Disable interrupts */ 00212 uint32_t old_INTCNTL = ITC->INTCNTL; 00213 ITC->INTCNTL = 0; 00214 00215 if (TMR0->ENBL & (1 << timer_num)) 00216 { 00217 /* Timer is enabled, so use the careful approach. 00218 Implemented in ASM so we can be sure of the cycle 00219 count */ 00220 uint32_t tmp1, tmp2; 00221 asm volatile (//".arm \n\t" 00222 "1: \n\t" 00223 "ldrh %[tmp1], %[comp] \n\t" // load COMP1 00224 "ldrh %[tmp2], %[count] \n\t" // load CNTR 00225 "sub %[tmp1], %[tmp1], %[tmp2] \n\t" // subtract 00226 "lsl %[tmp1], %[tmp1], #16 \n\t" // clear high bits 00227 "lsr %[tmp1], %[tmp1], #16 \n\t" 00228 "cmp %[tmp1], %[guard] \n\t" // compare to GUARD 00229 "bls 1b \n\t" // if less, goto 1 00230 00231 "strh %[ld1], %[cmpld] \n\t" // store CMPLD1 00232 "strh %[ld2], %[load] \n\t" // store LOAD 00233 : /* out */ 00234 [tmp1] "=&l" (tmp1), 00235 [tmp2] "=&l" (tmp2), 00236 [cmpld] "=m" (timer->CMPLD1), 00237 [load] "=m" (timer->LOAD) 00238 : /* in */ 00239 [comp] "m" (timer->COMP1), 00240 [count] "m" (timer->CNTR), 00241 [ld1] "l" (comp1), 00242 [ld2] "l" (load), 00243 [guard] "l" (pwm_info[timer_num].guard) 00244 : "memory" 00245 ); 00246 } else { 00247 /* Just set it directly, timer isn't running */ 00248 timer->CMPLD1 = comp1; 00249 timer->LOAD = load; 00250 } 00251 00252 /* Re-enable interrupts */ 00253 ITC->INTCNTL = old_INTCNTL; 00254 }