![](/media/cache/profiles/IMT_Graf_Marco_jpg.jpg.50x50_q85.jpg)
Fast 20MHz PWM, with double edge controlled PWMs (90° Phase shifted). CPU clock had to be adjusted to 80MHz (PLL-controlled). Done by accessing the registers directrly.
main.cpp@0:8bd0ebc5cbb0, 2010-06-09 (annotated)
- Committer:
- cedes
- Date:
- Wed Jun 09 07:47:00 2010 +0000
- Revision:
- 0:8bd0ebc5cbb0
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
cedes | 0:8bd0ebc5cbb0 | 1 | #include "mbed.h" |
cedes | 0:8bd0ebc5cbb0 | 2 | |
cedes | 0:8bd0ebc5cbb0 | 3 | DigitalOut myled1(LED1); |
cedes | 0:8bd0ebc5cbb0 | 4 | DigitalOut myled2(LED2); |
cedes | 0:8bd0ebc5cbb0 | 5 | DigitalOut myled3(LED3); |
cedes | 0:8bd0ebc5cbb0 | 6 | DigitalOut myled4(LED4); |
cedes | 0:8bd0ebc5cbb0 | 7 | |
cedes | 0:8bd0ebc5cbb0 | 8 | DigitalOut outGND1(p21); |
cedes | 0:8bd0ebc5cbb0 | 9 | PwmOut fmclck6(p22); |
cedes | 0:8bd0ebc5cbb0 | 10 | PwmOut fmclck5(p23); |
cedes | 0:8bd0ebc5cbb0 | 11 | DigitalOut outGND2(p24); |
cedes | 0:8bd0ebc5cbb0 | 12 | PwmOut fmclck4(p25); |
cedes | 0:8bd0ebc5cbb0 | 13 | |
cedes | 0:8bd0ebc5cbb0 | 14 | #define PLL0CFG_Val 0x00027 |
cedes | 0:8bd0ebc5cbb0 | 15 | #define PLL1_SETUP 0 |
cedes | 0:8bd0ebc5cbb0 | 16 | #define CCLKCFG_Val 5 |
cedes | 0:8bd0ebc5cbb0 | 17 | #define WAIT_SEC_BEFORE_START 5.0 |
cedes | 0:8bd0ebc5cbb0 | 18 | |
cedes | 0:8bd0ebc5cbb0 | 19 | int main() { |
cedes | 0:8bd0ebc5cbb0 | 20 | // Wait a bit before changing the clock. Who knows, perhaps |
cedes | 0:8bd0ebc5cbb0 | 21 | // it is needed to connect to the device |
cedes | 0:8bd0ebc5cbb0 | 22 | myled1 = 1; |
cedes | 0:8bd0ebc5cbb0 | 23 | wait(WAIT_SEC_BEFORE_START/4.0); |
cedes | 0:8bd0ebc5cbb0 | 24 | myled1 = 0; |
cedes | 0:8bd0ebc5cbb0 | 25 | myled2 = 1; |
cedes | 0:8bd0ebc5cbb0 | 26 | wait(WAIT_SEC_BEFORE_START/4.0); |
cedes | 0:8bd0ebc5cbb0 | 27 | myled2 = 0; |
cedes | 0:8bd0ebc5cbb0 | 28 | myled3 = 1; |
cedes | 0:8bd0ebc5cbb0 | 29 | wait(WAIT_SEC_BEFORE_START/4.0); |
cedes | 0:8bd0ebc5cbb0 | 30 | myled3 = 0; |
cedes | 0:8bd0ebc5cbb0 | 31 | myled4 = 1; |
cedes | 0:8bd0ebc5cbb0 | 32 | wait(WAIT_SEC_BEFORE_START/4.0); |
cedes | 0:8bd0ebc5cbb0 | 33 | myled1 = 1; |
cedes | 0:8bd0ebc5cbb0 | 34 | myled2 = 1; |
cedes | 0:8bd0ebc5cbb0 | 35 | myled3 = 1; |
cedes | 0:8bd0ebc5cbb0 | 36 | myled4 = 1; |
cedes | 0:8bd0ebc5cbb0 | 37 | |
cedes | 0:8bd0ebc5cbb0 | 38 | |
cedes | 0:8bd0ebc5cbb0 | 39 | |
cedes | 0:8bd0ebc5cbb0 | 40 | // The following sequence must be followed step by step in order to have PLL0 initialized |
cedes | 0:8bd0ebc5cbb0 | 41 | // and running: |
cedes | 0:8bd0ebc5cbb0 | 42 | |
cedes | 0:8bd0ebc5cbb0 | 43 | // 1. Disconnect PLL0 with one feed sequence if PLL0 is already connected. |
cedes | 0:8bd0ebc5cbb0 | 44 | LPC_SC->PLL0CON &= ~0x02; /* PLL0 Disconnect */ |
cedes | 0:8bd0ebc5cbb0 | 45 | LPC_SC->PLL0FEED = 0xAA; |
cedes | 0:8bd0ebc5cbb0 | 46 | LPC_SC->PLL0FEED = 0x55; |
cedes | 0:8bd0ebc5cbb0 | 47 | |
cedes | 0:8bd0ebc5cbb0 | 48 | // 2. Disable PLL0 with one feed sequence. |
cedes | 0:8bd0ebc5cbb0 | 49 | LPC_SC->PLL0CON &= ~0x01; /* PLL0 Disable */ |
cedes | 0:8bd0ebc5cbb0 | 50 | LPC_SC->PLL0FEED = 0xAA; |
cedes | 0:8bd0ebc5cbb0 | 51 | LPC_SC->PLL0FEED = 0x55; |
cedes | 0:8bd0ebc5cbb0 | 52 | |
cedes | 0:8bd0ebc5cbb0 | 53 | // 3. Change the CPU Clock Divider setting to speed up operation without PLL0, if desired. |
cedes | 0:8bd0ebc5cbb0 | 54 | // LPC_SC->CCLKCFG = 2; /* Setup Clock Divider "2 + 1 = 3 DIVIDER" */ |
cedes | 0:8bd0ebc5cbb0 | 55 | |
cedes | 0:8bd0ebc5cbb0 | 56 | // 4. Write to the Clock Source Selection Control register to change the clock source if |
cedes | 0:8bd0ebc5cbb0 | 57 | // needed. |
cedes | 0:8bd0ebc5cbb0 | 58 | LPC_SC->CLKSRCSEL = 0x00000001; /* Select Clock Source for PLL0 */ |
cedes | 0:8bd0ebc5cbb0 | 59 | |
cedes | 0:8bd0ebc5cbb0 | 60 | // 5. Write to the PLL0CFG and make it effective with one feed sequence. The PLL0CFG |
cedes | 0:8bd0ebc5cbb0 | 61 | // can only be updated when PLL0 is disabled. |
cedes | 0:8bd0ebc5cbb0 | 62 | LPC_SC->PLL0CFG = 0x00000013; /* configure PLL0, N = 1, M = 20 (0x13 + 1) */ |
cedes | 0:8bd0ebc5cbb0 | 63 | LPC_SC->PLL0FEED = 0xAA; |
cedes | 0:8bd0ebc5cbb0 | 64 | LPC_SC->PLL0FEED = 0x55; |
cedes | 0:8bd0ebc5cbb0 | 65 | |
cedes | 0:8bd0ebc5cbb0 | 66 | // 6. Enable PLL0 with one feed sequence. |
cedes | 0:8bd0ebc5cbb0 | 67 | LPC_SC->PLL0CON |= 0x01; /* PLL0 Enable */ |
cedes | 0:8bd0ebc5cbb0 | 68 | LPC_SC->PLL0FEED = 0xAA; |
cedes | 0:8bd0ebc5cbb0 | 69 | LPC_SC->PLL0FEED = 0x55; |
cedes | 0:8bd0ebc5cbb0 | 70 | |
cedes | 0:8bd0ebc5cbb0 | 71 | // 7. Change the CPU Clock Divider setting for the operation with PLL0. It is critical to do |
cedes | 0:8bd0ebc5cbb0 | 72 | // this before connecting PLL0. |
cedes | 0:8bd0ebc5cbb0 | 73 | LPC_SC->CCLKCFG = 5; /* Setup Clock Divider "5 + 1 = 6 DIVIDER" */ |
cedes | 0:8bd0ebc5cbb0 | 74 | |
cedes | 0:8bd0ebc5cbb0 | 75 | // 8. Wait for PLL0 to achieve lock by monitoring the PLOCK0 bit in the PLL0STAT register, |
cedes | 0:8bd0ebc5cbb0 | 76 | // or using the PLOCK0 interrupt, or wait for a fixed time when the input clock to PLL0 is |
cedes | 0:8bd0ebc5cbb0 | 77 | // slow (i.e. 32 kHz). The value of PLOCK0 may not be stable when the PLL reference |
cedes | 0:8bd0ebc5cbb0 | 78 | // frequency (FREF, the frequency of REFCLK, which is equal to the PLL input |
cedes | 0:8bd0ebc5cbb0 | 79 | // frequency divided by the pre-divider value) is less than 100 kHz or greater than |
cedes | 0:8bd0ebc5cbb0 | 80 | // 20 MHz. In these cases, the PLL may be assumed to be stable after a start-up time |
cedes | 0:8bd0ebc5cbb0 | 81 | // has passed. This time is 500 μs when FREF is greater than 400 kHz and 200 / FREF |
cedes | 0:8bd0ebc5cbb0 | 82 | // seconds when FREF is less than 400 kHz. |
cedes | 0:8bd0ebc5cbb0 | 83 | while (!(LPC_SC->PLL0STAT & (1<<26))); /* Wait for PLOCK0 */ |
cedes | 0:8bd0ebc5cbb0 | 84 | |
cedes | 0:8bd0ebc5cbb0 | 85 | // 9. Connect PLL0 with one feed sequence. |
cedes | 0:8bd0ebc5cbb0 | 86 | LPC_SC->PLL0CON |= 0x02; /* PLL0 Connect */ |
cedes | 0:8bd0ebc5cbb0 | 87 | LPC_SC->PLL0FEED = 0xAA; |
cedes | 0:8bd0ebc5cbb0 | 88 | LPC_SC->PLL0FEED = 0x55; |
cedes | 0:8bd0ebc5cbb0 | 89 | |
cedes | 0:8bd0ebc5cbb0 | 90 | // It is very important not to merge any steps above. For example, do not update the |
cedes | 0:8bd0ebc5cbb0 | 91 | // PLL0CFG and enable PLL0 simultaneously with the same feed sequence. |
cedes | 0:8bd0ebc5cbb0 | 92 | |
cedes | 0:8bd0ebc5cbb0 | 93 | |
cedes | 0:8bd0ebc5cbb0 | 94 | |
cedes | 0:8bd0ebc5cbb0 | 95 | // set PWM |
cedes | 0:8bd0ebc5cbb0 | 96 | outGND1 = 0; // Just to have a GND near to the PWMs |
cedes | 0:8bd0ebc5cbb0 | 97 | outGND2 = 0; // Just to have a GND near to the PWMs |
cedes | 0:8bd0ebc5cbb0 | 98 | LPC_PWM1->TCR = (1 << 1); // Reset counter, disable PWM |
cedes | 0:8bd0ebc5cbb0 | 99 | LPC_SC->PCLKSEL0 &= ~(0x3 << 12); |
cedes | 0:8bd0ebc5cbb0 | 100 | LPC_SC->PCLKSEL0 |= (1 << 12); // Set peripheral clock divider to /1, i.e. system clock |
cedes | 0:8bd0ebc5cbb0 | 101 | |
cedes | 0:8bd0ebc5cbb0 | 102 | LPC_PWM1->PCR |= 0x002C; // Double edge PWM for PWM2,3,5 |
cedes | 0:8bd0ebc5cbb0 | 103 | |
cedes | 0:8bd0ebc5cbb0 | 104 | LPC_PWM1->MR0 = 3; // Match Register 0 is shared period counter for all PWM1 |
cedes | 0:8bd0ebc5cbb0 | 105 | |
cedes | 0:8bd0ebc5cbb0 | 106 | LPC_PWM1->MR1 = 0; // so Match Register 0 |
cedes | 0:8bd0ebc5cbb0 | 107 | LPC_PWM1->MR2 = 2; // so Match Register 1 |
cedes | 0:8bd0ebc5cbb0 | 108 | LPC_PWM1->MR3 = 0; // so Match Register 2 |
cedes | 0:8bd0ebc5cbb0 | 109 | LPC_PWM1->MR4 = 1; // so Match Register 3 |
cedes | 0:8bd0ebc5cbb0 | 110 | LPC_PWM1->MR5 = 3; // so Match Register 3 |
cedes | 0:8bd0ebc5cbb0 | 111 | |
cedes | 0:8bd0ebc5cbb0 | 112 | LPC_PWM1->LER |= 1; // Start updating at next period start |
cedes | 0:8bd0ebc5cbb0 | 113 | LPC_PWM1->TCR = (1 << 0) || (1 << 3); // Enable counter and PWM |
cedes | 0:8bd0ebc5cbb0 | 114 | |
cedes | 0:8bd0ebc5cbb0 | 115 | while(1); |
cedes | 0:8bd0ebc5cbb0 | 116 | } |