Multiple Pulse Width Modulation on the Intel Quark microcontroller D1000

Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper October 2015 Document Number: 332918–001US You may not use o...
Author: Francis Morris
6 downloads 0 Views 900KB Size
Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper October 2015

Document Number: 332918–001US

You may not use or facilitate the use of this document in connection with any infringement or other legal analysis concerning Intel products described herein. You agree to grant Intel a non-exclusive, royalty-free license to any patent claim thereafter drafted which includes subject matter disclosed herein. No license (express or implied, by estoppel or otherwise) to any intellectual property rights is granted by this document. All information provided here is subject to change without notice. Contact your Intel representative to obtain the latest Intel product specifications and roadmaps. The products described may contain design defects or errors known as errata which may cause the product to deviate from published specifications. Current characterized errata are available on request. Copies of documents which have an order number and are referenced in this document may be obtained by calling 1-800-5484725 or by visiting: http://www.intel.com/design/literature.htm Intel, Intel® Quark™ and the Intel logo are trademarks of Intel Corporation in the U.S. and/or other countries. *Other names and brands may be claimed as the property of others. Copyright © 2015, Intel Corporation. All rights reserved.

Multiple Pulse Width Modulation on Intel® Quark™ microcontroller D1000 White Paper October 2015 2 Document Number: 332918–001US

Contents 1

Abstract ........................................................................................................... 5

2

Introduction ...................................................................................................... 6

3

Methods ........................................................................................................... 9

4

Results ............................................................................................................ 15

5

Discussion ....................................................................................................... 22

6

Conclusion ....................................................................................................... 23

7

References ....................................................................................................... 24

Figures Figure 1. Typical Servo Consists of Electric Motor, Gear Reduction, Closed Loop Controller, and 3wire Connector [2] ............................................................................................. 7 Figure 2. Typical Servo Control Input PWM Timing ..................................................................... 8 Figure 3. PWM Outputs in Cyan and Core Current in Red When Silicon Oscillator and General Purpose Timer Utilities Were Used................................................................................... 15 Figure 4. PWM Pulse Width Accuracy and Jitter When Silicon Oscillator and General Purpose Timer Utlities Were Used ............................................................................................ 16 Figure 5. PWM Outputs in Cyan and Core Current in Red With Silicon Oscillator and Custom Timer Interrupt Service Routines Coded in Assembler .................................................... 18 Figure 6. PWM Pulse Width Accuracy and Jitter With Silicon Oscillator and Custom Timer Interrupt Service Routines Coded in Assembler .................................................................. 19 Figure 7. PWM Outputs in Cyan and Core Current in Red With 33 MHz Crystal Oscillator and Custom Timer Interrupt Service Routines Coded in Assembler ........................................... 20 Figure 8. PWM Pulse Width Accuracy and Jitter with 33 MHz Crystal Oscillator and Custom Timer Interrupt Service Routines Coded in Assembler .................................................... 21

Tables Table 1. Maximum Cycle Frequency at Measured Interrupt Service Latencies and Various Resolutions22

Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper Document Number: 332918–001US

October 2015 3

Revision History Date

Revision

October 2015

001

Description Initial release.

§

Multiple Pulse Width Modulation on Intel® Quark™ microcontroller D1000 White Paper October 2015 4 Document Number: 332918–001US

1

Abstract A common use of microcontrollers is to control servos. Servos are actuators typically used in radio control and robotics. A pulse width modulated (PWM) signal commands rotation of the actuator. The Intel® Quark™ microcontroller D1000 does not have integrated PWM hardware, so a software solution is necessary. This paper presents a software solution for a multiple PWM controller. The methods used can implement PWMs to control up to 22 servos simultaneously with better than 3 µs resolution. This paper defines the requirements for controlling servos, develops a scalable solution, presents test results, and discussed the limitations of this implementation.

§

Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper Document Number: 332918–001US

October 2015 5

2

Introduction A typical servo is shown in Figure 1. It consists of a lever (on top) attached to an electric motor through a gear reduction, a closed loop controller, and a 3-wire connector. The 3-wire connector supplies power, ground and a single-ended PWM control signal. The angle of the actuator is controlled by the pulse width of the PWM signal as shown in Figure 2. The period of the PWM signal is generally fixed, but varies from servo to servo and can be as little as 6 ms to as much as 25 ms [1]. A PWM signal can be generated by software bit banging a digital GPIO. However, precisely controlling period and duty cycle requires the use of timers. The Intel® Quark™ microcontroller D1000 has three timers. One uses the system clock frequency. The other two use a programmable division of the oscillator frequency and are independent of system clock frequency. Using the lower frequency timers reduces power consumption. The resolution of the PWM (for example, the size of the discrete time increments by which the pulse width is varied) is limited by the time required to process a change in timer value. One can imagine a software implementation that would process every change in timer value. When the new timer value corresponds to a time that the PWM output should change, software toggles the GPIO. However, this implementation results in software processing every discrete time change even though there only two times of interest: (1) start of cycle and (2) width of pulse. For the finest time resolution, software will be constantly processing and never sleeping. A better implementation uses two timers: (1) a periodic cycle timer and (2) a one shot pulse width timer. When the cycle timer expires, the PWM output is driven high and the pulse width timer is started. When the pulse width timer expires, the PWM output is driven low. Between these times, the processor sleeps or performs background tasks.

Multiple Pulse Width Modulation on Intel® Quark™ microcontroller D1000 White Paper October 2015 6 Document Number: 332918–001US

Figure 1. Typical Servo Consists of Electric Motor, Gear Reduction, Closed Loop Controller, and 3-wire Connector [2]

Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper Document Number: 332918–001US

October 2015 7

Figure 2. Typical Servo Control Input PWM Timing

20.0 ms 1.5 ms

2.0 ms

1.0 ms

§

Multiple Pulse Width Modulation on Intel® Quark™ microcontroller D1000 White Paper October 2015 8 Document Number: 332918–001US

3

Methods The implementation in this paper is based on the two-timer solution, but the solutionis extended to a more general case of multiple PWM outputs. The number of PWMs is limited by the number of GPIO pins available. Reserving at least two pins for communication, up to 22 PWM signals can be generated using this implementation with no detriment to performance. In order to simplify things, this paper discusses an eight PWM implementation. However, it should be obvious to the reader how to scale the implementation for fewer or more PWMs. Precise timing requires precise frequency. The Intel® Quark™ microcontroller D1000 supports crystal oscillators from 20-33 MHz for the system clock and 32.768 kHz for the real time clock. Optionally, a silicon oscillator can be used for the system clock to save power. However, the accuracy of the silicon oscillator can be affected by temperature. The silicon oscillator should be calibrated before use. Fortunately, the Intel® Quark™ microcontroller D1000 has an oscillator calibration utility in ROM that will calibrate the silicon oscillator to the real time clock oscillator with 10-bit precision (for example, 0) { #endif

The O33M_FREQUENCY symbol is defined in the Quark_D1000_CfgDefs.h header file. For best resolution, it is defined as 32 MHz. The next step is to initialize the hardware environment. The code snippet shown in the following selects clock dividers for the timers, enables APB and timer clocks, powers down the real time clock oscillator, and selects the GPIO pins to use for PWM outputs. Symbols T0_RATIO and T1_RATIO are defined in the Quark_D1000_CfgDefs.h header file. As shown later, the best achievable timing resolution is >1 µs, so the lowest timer clock frequency that will provide 1 µs resolution (for example, the divide by 32 clock) was chosen.

Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper Document Number: 332918–001US

October 2015 9

// initialize hardware PM_CLK_SEL.f = (PM_CLK_SEL_bits_s) { .T0_CLKRATIO = T0_RATIO, // timer 0 clock ratio .T1_CLKRATIO = T1_RATIO}; // timer 1 clock ratio PM_CLK_EN.f = (PM_CLK_EN_bits_s) { .APB_CLKEN = 1, // APB clock enabled .T0_CLKEN = 1, // timer 0 clock enabled .T1_CLKEN = 1, // timer 1 clock enabled .RTC_OPD = 1}; // RTC oscillator powered down GI_SWPORTA_DDR = 0xF0F; // GPIO[11:8,3:0] are outputs PM_IOP_FUNC_CTL0.f = (PM_IOP_FUNC_CTL0_bits_s) { .PF0 = 1, // pin function 0 is GPIO .PF1 = 1, // pin function 1 is GPIO .PF2 = 1, // pin function 2 is GPIO .PF3 = 1, // pin function 3 is GPIO .PF4 = 3, // pin function 4 is analog .PF5 = 3, // pin function 5 is analog .PF6 = 3, // pin function 6 is analog .PF7 = 3}; // pin function 7 is analog PM_IOP_FUNC_CTL1.f = (PM_IOP_FUNC_CTL1_bits_s) { .PF8 = 1, // pin function 8 is GPIO .PF9 = 1, // pin function 9 is GPIO .PF10 = 1, // pin function 10 is GPIO .PF11 = 1, // pin function 11 is GPIO .PF12 = 3, // pin function 12 is analog .PF13 = 3, // pin function 13 is analog .PF14 = 3, // pin function 14 is analog .PF15 = 3}; // pin function 15 is analog

The next step is to start a periodic timer and register a callback function for it. The code snippet that follows sets the timer period in timer clock units to the timer frequency divided by the desired PWM frequency (for example, 500 Hz). The interrupt from this timer marks the start of a PWM cycle. // register timer callbacks timer_0_callback(timer_0_callback_function); // PWM period timer start_periodic_timer_0(T0_FREQUENCY/PWM_FREQUENCY);

The last stage is to enable interrupts and enter the main loop. The code needed to update PWM settings is application-specific. The control inputs could come from a sensor, a controller or a radio; a discussion of the particular algorithm needed is outside the scope of this paper. The focus here is the software implementation of the PWM. Suffice it to say that whatever the algorithm is, it must be executed as a background task so that it does not introduce any latency in the timer interrupt service. If other interrupts need to be serviced (such as UART, SPI, or I²C), the trap (TR) bit in the IDT entry for that interrupt should be set. This will allow the lower priority interrupt to be preempted by a timer interrupt. Once PWM updates have been processed, there is nothing left to do but wait. Here the code executes a halt instruction to put the processor, AMBA fabric and memory into a clock gated state for power reduction.

Multiple Pulse Width Modulation on Intel® Quark™ microcontroller D1000 White Paper October 2015 10 Document Number: 332918–001US

// enable interrupts __asm__("sti"); // loop forever while (1) { /************************************************************************* This is where you would process changes to the PWM duty cycle. Do this in the background so that it can be interrupted. Use a ping-pong approach to manage updates to the pwm_state, pwm_duty_cycle and pwm_last_update tables or wait until all PWM outputs are low and update the tables before the next PWM cycle begins. *************************************************************************/ // halt and wait for interrupt __asm__("hlt"); } } }

Information on PWM state and duty cycles must be passed from the PWM update algorithm to the timer callback functions. This application does this with global arrays. The PWM state array shown in the following example defines the state of the PWM outputs at each of the pulse width timer events. It must be sorted by shortest to longest pulse width. It could be from one to eight entries long depending on how many PWM outputs share the same pulse width. Each time a pulse width timer expires one or more PWM outputs are driven low. In the initial state of this array PWM 0 has the shortest pulse with and PWMs 1-7 have successively longer pulse widths. //////////////////////////////////////////////////////////////////////////////// // This is the PWM output state table. Each array entry contains the // output state for the eight PWM outputs. There can be up to nine // unique output states. //////////////////////////////////////////////////////////////////////////////// uint32_t pwm_state[] = { 0x00F0E, // GPIO[0] = PWM[0] = 0 0x00F0C, // GPIO[1] = PWM[1] = 0 0x00F08, // GPIO[2] = PWM[2] = 0 0x00F00, // GPIO[3] = PWM[3] = 0 0x00E00, // GPIO[8] = PWM[4] = 0 0x00C00, // GPIO[9] = PWM[5] = 0 0x00800, // GPIO[10] = PWM[6] = 0 0x00000};// GPIO[11] = PWM[7] = 0;

This application uses a Boolean array to indicate the final PWM state entry. The code could eliminate this array entirely and rely on a particular GPIO state (for example, all zeroes), but this application uses this array in order to keep the solution general and allow other non-PWM GPIOs to take arbitrary values.

Multiple Pulse Width Modulation on the Intel® Quark™ microcontroller D1000 White Paper Document Number: 332918–001US

October 2015 11

//////////////////////////////////////////////////////////////////////////////// // This is the last update flag table. Each array entry corresponds to // an entry in the PWM output state array. A non-zero value indicates // which array entry contains the final output state. //////////////////////////////////////////////////////////////////////////////// uint32_t pwm_last_update[] = { 0, 0, 0, 0, 0, 0, 0, 1};

The application needs an array of pulse widths (or duty cycles) for each unique PWM pulse. The values in this array are actually the amount of time the PWMs remain low after having generated their high pulse. At each PWM state update, this value is subtracted from the amount of time remaining in the PWM cycle. The difference is the time until the next PWM update (for example, the next PWM falling edge). Like the PWM state array, there could be from one to eight entries depending on how many PWM outputs share the same pulse width. Also, this array must be sorted from shortest to longest pulse width. //////////////////////////////////////////////////////////////////////////////// // This is the PWM duty cycle table. Each array entry contains a duty // cycle expressed as duration of low state in timer clock // units. There can be up to eight duty cycles (one for each PWM // output). Entries are sorted from highest to lowest. //////////////////////////////////////////////////////////////////////////////// uint32_t pwm_duty_cycle[] = { T0_FREQUENCY/PWM_FREQUENCY - 1500, // 1500 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1505, // 1505 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1510, // 1510 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1515, // 1515 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1520, // 1520 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1525, // 1525 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1530, // 1530 us pulse T0_FREQUENCY/PWM_FREQUENCY - 1535};// 1535 us pulse

Finally, the application needs a global variable to index the PWM state and duty cycle arrays. This variable is cleared at the start of each PWM cycle and increments with each PWM state update. //////////////////////////////////////////////////////////////////////////////// // This is the index into the PWM state array. It points to the next PWM state. //////////////////////////////////////////////////////////////////////////////// uint32_t state_index = 0;

Multiple Pulse Width Modulation on Intel® Quark™ microcontroller D1000 White Paper October 2015 12 Document Number: 332918–001US

The cycle timer initiates the start of each PWM cycle. The code snippet shown in the following example shows the tasks that must be completed at start of each PWM cycle. All PWM outputs must be driven high. The pulse width timer must be started with the relative time to the shortest PWM pulse. The PWM state index must be reset to the top of the PWM state table. Finally, the pulse width timer callback must be registered and the cycle timer callback must be re-registered. //////////////////////////////////////////////////////////////////////////////// // this is the timer0 callback function //////////////////////////////////////////////////////////////////////////////// void timer_0_callback_function(void) { // update PWM outputs GI_SWPORTA_DR = 0xF0F; // enable pulse width timer clock PM_CLK_EN.f.T1_CLKEN = 1; // load count relative to start of cycle T1_LOAD_CNT = T0_CRNT_VAL - pwm_duty_cycle[0]; // enable pulse width timer T1_CTL_REG.all = ((0