A few videos of flashing/fading LEDs:
-
-
Save cdzombak/70706902542b0de3fda1 to your computer and use it in GitHub Desktop.
Working "Knight Rider" light show & PWM example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* "Knight Rider" lights | |
* Using timer and GPIO peripherals; PWM on GPIO. | |
* | |
* Chris Dzombak <cdzombak@umich.edu> | |
* EECS373 Winter 2011 | |
* | |
* @TODO Should I set timer ISR at runtime by writing to the vector table? | |
* Or should I use the default ISR in the startup assembly and branch to here? | |
*/ | |
#include <inttypes.h> | |
#include "gpio.h" | |
#include "tim1.h" | |
#define ENABLE_INTERRUPTS __asm__ __volatile__ ("cpsie i"); | |
#define TICK_PERIOD 1e3 // timer is 100 MHz; this gives us an interrupt (timer tick) at 100 KHz | |
#define PWM_PERIOD 1e2 // PWM period = 100 timer ticks. works out to 1KHz PWM | |
#define KNIGHT_CHANGE_PERIOD 7000 | |
#define NUM_LEDS 8 | |
#define ALL_LED_MASK 0xff000000 | |
#define LED0_MASK 0x01000000 | |
#define LED1_MASK 0x02000000 | |
#define LED2_MASK 0x04000000 | |
#define LED3_MASK 0x08000000 | |
#define LED4_MASK 0x10000000 | |
#define LED5_MASK 0x20000000 | |
#define LED6_MASK 0x40000000 | |
#define LED7_MASK 0x80000000 | |
const uint32_t LED_MASK[NUM_LEDS] = { | |
LED0_MASK, LED1_MASK, LED2_MASK, LED3_MASK, | |
LED4_MASK, LED5_MASK, LED6_MASK, LED7_MASK | |
}; | |
const uint32_t DUTY_CYCLE[(2*NUM_LEDS) - 1] = { | |
1000, 90, 40, 25, 15, 10, 8, 6, 5, 4, 3, 2, 1, 0, 0 | |
}; | |
/* PWM is a global counter because my PWM logic needs to know | |
exactly where we are in the PWM period. */ | |
static volatile uint32_t currentPWM = 0; | |
static volatile uint8_t knightChangeFlag = 0; | |
int main () | |
{ | |
int8_t i = 0; // loop variable | |
uint32_t currentGPIOState = ALL_LED_MASK; // write 1 (off) to all LEDs | |
uint8_t currentStep[NUM_LEDS]; | |
int8_t knightDirection = 1; | |
uint8_t knightPrimary = 0; | |
currentStep[0] = 0; | |
currentStep[1] = 1; | |
currentStep[2] = 2; | |
currentStep[3] = 3; | |
currentStep[4] = 4; | |
currentStep[5] = 5; | |
currentStep[6] = 6; | |
currentStep[7] = 7; | |
// init GPIO and turn all LEDs off | |
initGPIO(24); | |
initGPIO(25); | |
initGPIO(26); | |
initGPIO(27); | |
initGPIO(28); | |
initGPIO(29); | |
initGPIO(30); | |
initGPIO(31); | |
setGPIO(currentGPIOState); | |
/* enable interrupts globally */ | |
ENABLE_INTERRUPTS | |
// set up timer to give us an interrupt every TICK_PERIOD | |
tim1_enable(); | |
tim1_set_mode(0); | |
tim1_enable_interrupts(); | |
tim1_start(TICK_PERIOD); | |
while(1) { | |
if (knightChangeFlag != 0) { | |
knightChangeFlag = 0; | |
for (i=0; i<NUM_LEDS; i++) { | |
currentStep[i]++; | |
} | |
if (knightPrimary == 0) knightDirection = 1; | |
if (knightPrimary >= (NUM_LEDS - 1)) knightDirection = -1; | |
knightPrimary += knightDirection; | |
currentStep[knightPrimary] = 0; | |
} | |
for (i=0; i<NUM_LEDS; i++) { | |
if ( DUTY_CYCLE[currentStep[i]] >= currentPWM ) { | |
// write 0 to turn LED on | |
currentGPIOState &= (~(LED_MASK[i])); | |
} else { | |
// write 1 to turn LED off | |
currentGPIOState |= LED_MASK[i]; | |
} | |
} | |
setGPIO(currentGPIOState); | |
} | |
} | |
void my_tim1_isr() { | |
static uint32_t knightChangeCount = 0; | |
tim1_clear_interrupt(); | |
currentPWM++; | |
knightChangeCount++; | |
if (currentPWM >= PWM_PERIOD) { | |
currentPWM = 0; | |
} | |
if (knightChangeCount >= KNIGHT_CHANGE_PERIOD) { | |
knightChangeCount = 0; | |
knightChangeFlag = 1; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* PWM example | |
* Using timer and GPIO peripherals; PWM done on GPIO. | |
* | |
* Chris Dzombak <cdzombak@umich.edu> | |
* EECS373 Winter 2011 | |
* | |
* @TODO Should I set timer ISR at runtime by writing to the vector table? | |
* Or should I use the default ISR in the startup assembly and branch to here? | |
*/ | |
#include <inttypes.h> | |
#include "gpio.h" | |
#include "tim1.h" | |
#define TICK_PERIOD 1e3 // timer is 100 MHz; this gives us an interrupt (timer tick) at 100 KHz | |
#define PWM_PERIOD 1e3 // PWM period = 1000 timer ticks. works out to 100 Hz PWM | |
#define PWM_SCALE 10 // value needed to scale percentages up to PWM period | |
#define NUM_LEDS 8 | |
#define ALL_LED_MASK 0xff000000 | |
#define LED0_MASK 0x01000000 | |
#define LED1_MASK 0x02000000 | |
#define LED2_MASK 0x04000000 | |
#define LED3_MASK 0x08000000 | |
#define LED4_MASK 0x10000000 | |
#define LED5_MASK 0x20000000 | |
#define LED6_MASK 0x40000000 | |
#define LED7_MASK 0x80000000 | |
const uint32_t LED_MASK[NUM_LEDS] = { | |
LED0_MASK, LED1_MASK, LED2_MASK, LED3_MASK, | |
LED4_MASK, LED5_MASK, LED6_MASK, LED7_MASK | |
}; | |
/* PWM is a global counter because my PWM logic needs to know | |
exactly where we are in the PWM period. */ | |
static volatile uint32_t currentPWM = 0; | |
int main() { | |
int8_t i = 0; // loop variable | |
uint32_t currentGPIOState = ALL_LED_MASK; // write 1 (off) to all LEDs | |
uint32_t currentDutyCycle[NUM_LEDS]; | |
currentDutyCycle[0] = 100 * PWM_SCALE; | |
currentDutyCycle[1] = 40 * PWM_SCALE; | |
currentDutyCycle[2] = 30 * PWM_SCALE; | |
currentDutyCycle[3] = 25 * PWM_SCALE; | |
currentDutyCycle[4] = 20 * PWM_SCALE; | |
currentDutyCycle[5] = 15 * PWM_SCALE; | |
currentDutyCycle[6] = 10 * PWM_SCALE; | |
currentDutyCycle[7] = 5 * PWM_SCALE; | |
// init GPIO and turn all LEDs off | |
initGPIO(24); | |
initGPIO(25); | |
initGPIO(26); | |
initGPIO(27); | |
initGPIO(28); | |
initGPIO(29); | |
initGPIO(30); | |
initGPIO(31); | |
setGPIO(currentGPIOState); | |
/* enable interrupts globally */ | |
asm volatile ("cpsie i"); | |
// set up timer to give us an interrupt every TICK_PERIOD | |
tim1_enable(); | |
tim1_set_mode(0); | |
tim1_enable_interrupts(); | |
tim1_start(TICK_PERIOD); | |
while(1) { | |
for (i=0; i<NUM_LEDS; i++) { | |
if ( currentDutyCycle[i] >= currentPWM ) { | |
// write 0 to turn LED on | |
currentGPIOState &= (~(LED_MASK[i])); | |
} else { | |
// write 1 to turn LED off | |
currentGPIOState |= LED_MASK[i]; | |
} | |
} | |
setGPIO(currentGPIOState); | |
} | |
} | |
void my_tim1_isr() { | |
tim1_clear_interrupt(); | |
currentPWM++; | |
if (currentPWM >= PWM_PERIOD) { | |
currentPWM = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment