Create a gist now

Instantly share code, notes, and snippets.

@cdzombak /PWM.c Secret
Created Jan 21, 2011

What would you like to do?
Working "Knight Rider" light show & PWM example
/**
* "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;
}
}
/**
* 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