Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: STM32F3-Discovery
neopixel.c
- Committer:
- MartinJohnson
- Date:
- 2016-05-25
- Revision:
- 0:d89511b21e3d
File content as of revision 0:d89511b21e3d:
#include <stm32f30x.h>
//#include <stm32f30x_misc.h>
//#define ARM_MATH_CM4
#include <math.h>
#include "stm32f3_discovery_lsm303dlhc.h"
#include <stdlib.h>
#define WS2812_GPIO GPIOB
#define WS2812_GPIO_AHB_PERIPHERAL RCC_AHBPeriph_GPIOB
#define WS2812_GPIO_AF GPIO_AF_1
#define WS2812_PIN GPIO_Pin_8 // TIM16_CH1
#define WS2812_PIN_SOURCE GPIO_PinSource8
#define WS2812_TIMER TIM16
#define WS2812_TIMER_APB2_PERIPHERAL RCC_APB2Periph_TIM16
#define WS2812_DMA_CHANNEL DMA1_Channel3
#define WS2812_IRQ DMA1_Channel3_IRQn
#define WS2812_DMA_TC_FLAG DMA1_FLAG_TC3
#define PI 3.1415926f
#define WS2812_LED_STRIP_LENGTH 60
#define WS2812_BITS_PER_LED 24
#define WS2812_DELAY_BUFFER_LENGTH 42 // for 50us delay
#define BIT_COMPARE_1 17 // timer compare value for logical 1
#define BIT_COMPARE_0 9 // timer compare value for logical 0
#define WS2812_DATA_BUFFER_SIZE (WS2812_BITS_PER_LED * WS2812_LED_STRIP_LENGTH)
#define WS2812_DMA_BUFFER_SIZE (WS2812_DATA_BUFFER_SIZE + WS2812_DELAY_BUFFER_LENGTH) // number of bytes needed is #LEDs * 24 bytes + 42 trailing bytes)
static volatile uint8_t ledStripDMABuffer[WS2812_DMA_BUFFER_SIZE];
//static volatile uint8_t rgbData[WS2812_LED_STRIP_LENGTH*3];
static volatile uint8_t WS2812LedDataTransferInProgress = 0;
static uint16_t dmaBufferOffset;
static int16_t ledIndex;
volatile unsigned sysTiming;
volatile unsigned sysTicks = 0;
int sysInitSystemTimer(void) {
if (SysTick_Config((SystemCoreClock / 1000) -1 )) {
return(0);
}
return(1);
}
void sysDelayMs(unsigned dly) {
sysTiming = dly;
while (sysTiming > 0) __wfi();
}
void SysTick_Handler(void) {
sysTicks++;
if (sysTiming > 0) --sysTiming;
}
void AccelerometerConfig(void)
{
LSM303DLHCMag_InitTypeDef LSM303DLHC_InitStructure;
LSM303DLHCAcc_InitTypeDef LSM303DLHCAcc_InitStructure;
LSM303DLHCAcc_FilterConfigTypeDef LSM303DLHCFilter_InitStructure;
/* Configure MEMS magnetometer main parameters: temp, working mode, full Scale and Data rate */
LSM303DLHC_InitStructure.Temperature_Sensor = LSM303DLHC_TEMPSENSOR_DISABLE;
LSM303DLHC_InitStructure.MagOutput_DataRate =LSM303DLHC_ODR_30_HZ ;
LSM303DLHC_InitStructure.MagFull_Scale = LSM303DLHC_FS_8_1_GA;
LSM303DLHC_InitStructure.Working_Mode = LSM303DLHC_CONTINUOS_CONVERSION;
LSM303DLHC_MagInit(&LSM303DLHC_InitStructure);
/* Fill the accelerometer structure */
LSM303DLHCAcc_InitStructure.Power_Mode = LSM303DLHC_NORMAL_MODE;
LSM303DLHCAcc_InitStructure.AccOutput_DataRate = LSM303DLHC_ODR_50_HZ;
LSM303DLHCAcc_InitStructure.Axes_Enable= LSM303DLHC_AXES_ENABLE;
LSM303DLHCAcc_InitStructure.AccFull_Scale = LSM303DLHC_FULLSCALE_2G;
LSM303DLHCAcc_InitStructure.BlockData_Update = LSM303DLHC_BlockUpdate_Continous;
LSM303DLHCAcc_InitStructure.Endianness=LSM303DLHC_BLE_LSB;
LSM303DLHCAcc_InitStructure.High_Resolution=LSM303DLHC_HR_ENABLE;
/* Configure the accelerometer main parameters */
LSM303DLHC_AccInit(&LSM303DLHCAcc_InitStructure);
/* Fill the accelerometer LPF structure */
LSM303DLHCFilter_InitStructure.HighPassFilter_Mode_Selection =LSM303DLHC_HPM_NORMAL_MODE;
LSM303DLHCFilter_InitStructure.HighPassFilter_CutOff_Frequency = LSM303DLHC_HPFCF_16;
LSM303DLHCFilter_InitStructure.HighPassFilter_AOI1 = LSM303DLHC_HPF_AOI1_DISABLE;
LSM303DLHCFilter_InitStructure.HighPassFilter_AOI2 = LSM303DLHC_HPF_AOI2_DISABLE;
/* Configure the accelerometer LPF main parameters */
LSM303DLHC_AccFilterConfig(&LSM303DLHCFilter_InitStructure);
}
void ReadAccelerometer(float * data)
{
uint8_t buffer[6];
LSM303DLHC_Read(ACC_I2C_ADDRESS, LSM303DLHC_OUT_X_L_A, buffer, 6);
for(int i=0; i<3; i++) {
data[i]=(float)((int16_t)((uint16_t)buffer[2*i+1] << 8) + buffer[2*i])/16;
}
}
void ReadMagnetometer (float* pfData)
{
static uint8_t buffer[6] = {0};
uint8_t CTRLB = 0;
uint16_t Magn_Sensitivity_XY = 0, Magn_Sensitivity_Z = 0;
uint8_t i =0;
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_CRB_REG_M, &CTRLB, 1);
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_OUT_X_H_M, buffer, 1);
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_OUT_X_L_M, buffer+1, 1);
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_OUT_Y_H_M, buffer+2, 1);
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_OUT_Y_L_M, buffer+3, 1);
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_OUT_Z_H_M, buffer+4, 1);
LSM303DLHC_Read(MAG_I2C_ADDRESS, LSM303DLHC_OUT_Z_L_M, buffer+5, 1);
/* Switch the sensitivity set in the CRTLB*/
switch(CTRLB & 0xE0)
{
case LSM303DLHC_FS_1_3_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_1_3Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_1_3Ga;
break;
case LSM303DLHC_FS_1_9_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_1_9Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_1_9Ga;
break;
case LSM303DLHC_FS_2_5_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_2_5Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_2_5Ga;
break;
case LSM303DLHC_FS_4_0_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_4Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_4Ga;
break;
case LSM303DLHC_FS_4_7_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_4_7Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_4_7Ga;
break;
case LSM303DLHC_FS_5_6_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_5_6Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_5_6Ga;
break;
case LSM303DLHC_FS_8_1_GA:
Magn_Sensitivity_XY = LSM303DLHC_M_SENSITIVITY_XY_8_1Ga;
Magn_Sensitivity_Z = LSM303DLHC_M_SENSITIVITY_Z_8_1Ga;
break;
}
for(i=0; i<2; i++)
{
pfData[i]=(float)((int16_t)(((uint16_t)buffer[2*i] << 8) + buffer[2*i+1])*1000)/Magn_Sensitivity_XY;
}
pfData[2]=(float)((int16_t)(((uint16_t)buffer[4] << 8) + buffer[5])*1000)/Magn_Sensitivity_Z;
}
void GPIO_init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
// GPIOA Periph clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOE, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(WS2812_TIMER_APB2_PERIPHERAL, ENABLE);
sysInitSystemTimer();
/* Configuration alternate function push-pull */
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = WS2812_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(WS2812_GPIO, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_1); // pb8 is tim16 ch1
GPIO_InitStructure.GPIO_Pin =
GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 |
GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIOE->ODR ^= 0x100;
}
void TIM_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// NVIC_InitTypeDef NVIC_InitStructure;
uint16_t PrescalerValue;
PrescalerValue = (uint16_t) (SystemCoreClock / 24000000) - 1;
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = 29; // 800kHz
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(WS2812_TIMER, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration */
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(WS2812_TIMER, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(WS2812_TIMER, TIM_OCPreload_Enable);
TIM_CtrlPWMOutputs(WS2812_TIMER, ENABLE);
}
void DMA_init(void) {
DMA_InitTypeDef DMA_InitStructure;
/* configure DMA */
/* DMA clock enable */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA Channel Config */
DMA_DeInit(WS2812_DMA_CHANNEL);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & WS2812_TIMER->CCR1;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) ledStripDMABuffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = WS2812_DMA_BUFFER_SIZE;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(WS2812_DMA_CHANNEL, &DMA_InitStructure);
TIM_DMACmd(WS2812_TIMER, TIM_DMA_CC1, ENABLE);
DMA_ITConfig(WS2812_DMA_CHANNEL, DMA_IT_TC, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = WS2812_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void WS2812LedStripDMAEnable(void) {
DMA_SetCurrDataCounter(WS2812_DMA_CHANNEL,
WS2812_DMA_BUFFER_SIZE); // load number of bytes to be transferred
TIM_SetCounter(WS2812_TIMER, 0);
TIM_Cmd(WS2812_TIMER, ENABLE);
DMA_Cmd(WS2812_DMA_CHANNEL, ENABLE);
}
void DMA1_Channel3_IRQHandler(void) {
// GPIOE->ODR ^= 0x1000;
if (DMA_GetFlagStatus(WS2812_DMA_TC_FLAG)) {
WS2812LedDataTransferInProgress = 0;
DMA_Cmd(WS2812_DMA_CHANNEL, DISABLE);
DMA_ClearFlag(WS2812_DMA_TC_FLAG);
}
}
void updateLEDDMABuffer(uint8_t componentValue) {
uint8_t bitIndex;
for (bitIndex = 0; bitIndex < 8; bitIndex++) {
if ((componentValue << bitIndex) &
0x80) // data sent MSB first, j = 0 is MSB j = 7 is LSB
{
ledStripDMABuffer[dmaBufferOffset] = BIT_COMPARE_1;
}
else {
ledStripDMABuffer[dmaBufferOffset] = BIT_COMPARE_0; // compare value for logical 0
}
dmaBufferOffset++;
}
}
typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} rgb_struct;
static volatile rgb_struct rgbData[WS2812_LED_STRIP_LENGTH];
void WS2812UpdateStrip(void) {
static uint32_t waitCounter = 0;
// wait until previous transfer completes
while (WS2812LedDataTransferInProgress) {
waitCounter++;
}
dmaBufferOffset = 0; // reset buffer memory index
ledIndex = 0; // reset led index
// fill transmit buffer with correct compare values to achieve
// correct pulse widths according to color values
while (ledIndex < WS2812_LED_STRIP_LENGTH) {
updateLEDDMABuffer(rgbData[ledIndex].green);
updateLEDDMABuffer(rgbData[ledIndex].red);
updateLEDDMABuffer(rgbData[ledIndex++].blue);
}
WS2812LedDataTransferInProgress = 1;
WS2812LedStripDMAEnable();
}
extern volatile unsigned sysTicks;
int rndseed=0;
/*
void srand(int i) {
rndseed = i;
}
int rand() {
return (((rndseed = rndseed * 214013L + 2531011L) >> 16) &
0x7fff);
}
*/
void WS2812LedStripInit(void) {
int i;
for (i = 0; i < WS2812_DMA_BUFFER_SIZE; i++)
ledStripDMABuffer[i] = 0;
WS2812UpdateStrip();
}
static float AccBuffer[3] = {0.0f};
static float MagBuffer[3] = {0.0f};
//extern float arm_sqrt_f32(float f);
int getheading() {
/* Read Compass data */
ReadMagnetometer(MagBuffer);
ReadAccelerometer(AccBuffer);
int i;
float fNormAcc, fSinRoll, fCosRoll, fSinPitch, fCosPitch = 0.0f, RollAng = 0.0f, PitchAng = 0.0f;
float fTiltedX, fTiltedY = 0.0f;
float HeadingValue;
for (i = 0; i < 3; i++)
AccBuffer[i] /= 100.0f;
fNormAcc = sqrtf((AccBuffer[0] * AccBuffer[0]) + (AccBuffer[1] * AccBuffer[1]) +
(AccBuffer[2] * AccBuffer[2]));
fSinRoll = -AccBuffer[1] / fNormAcc;
fCosRoll = sqrtf(1.0f - (fSinRoll * fSinRoll));
fSinPitch = AccBuffer[0] / fNormAcc;
fCosPitch = sqrtf(1.0f - (fSinPitch * fSinPitch));
if (fSinRoll > 0) {
if (fCosRoll > 0) {
RollAng = acos(fCosRoll) * 180 / PI;
}
else {
RollAng = acos(fCosRoll) * 180 / PI + 180;
}
}
else {
if (fCosRoll > 0) {
RollAng = acos(fCosRoll) * 180 / PI + 360;
}
else {
RollAng = acos(fCosRoll) * 180 / PI + 180;
}
}
if (fSinPitch > 0) {
if (fCosPitch > 0) {
PitchAng = acos(fCosPitch) * 180 / PI;
}
else {
PitchAng = acos(fCosPitch) * 180 / PI + 180;
}
}
else {
if (fCosPitch > 0) {
PitchAng = acos(fCosPitch) * 180 / PI + 360;
}
else {
PitchAng = acos(fCosPitch) * 180 / PI + 180;
}
}
if (RollAng >= 360) {
RollAng = RollAng - 360;
}
if (PitchAng >= 360) {
PitchAng = PitchAng - 360;
}
fTiltedX = MagBuffer[0] * fCosPitch + MagBuffer[2] * fSinPitch;
fTiltedY = MagBuffer[0] * fSinRoll * fSinPitch + MagBuffer[1] * fCosRoll -
MagBuffer[1] * fSinRoll * fCosPitch;
// fTiltedX=fTiltedX/sqrt(fTiltedX*fTiltedX+fTiltedY*fTiltedY);
HeadingValue = (float) ((atan2f( (float)fTiltedY, (float)fTiltedX)) * 180) / PI;
if (HeadingValue < 0) {
HeadingValue = HeadingValue + 360;
}
return HeadingValue;
}
int main(void) {
uint8_t i = 0;
int j=0;
int delay=2000;
int dch=-1;
int dir=1;
int btn=0;
uint32_t rgb = 0x0ff0000ff;
GPIO_init();
GPIOE->ODR ^= 0x100;
DMA_init();
GPIOE->ODR ^= 0x200;
TIM_init();
AccelerometerConfig();
WS2812LedDataTransferInProgress = 0;
WS2812LedStripInit();
btn=GPIOA->IDR;
if((GPIOA->IDR & 1)==1) {
// a giant compass!
while (1) {
int heading = getheading();
int led = (heading * WS2812_LED_STRIP_LENGTH) / 360;
int next = (led + 1) % WS2812_LED_STRIP_LENGTH;
int v2 = (heading - (led * 360) / WS2812_LED_STRIP_LENGTH) * 10;
int v1 = 225 - v2;
rgbData[led].red = v1;
rgbData[next].red = v2;
WS2812UpdateStrip();
// wait 5ms
sysDelayMs(15);
rgbData[led].red = 0;
rgbData[next].red = 0;
}
}
while (1) { // flashing lights moving along the strip
rgbData[j].red = (rgb & 255); // green
rgbData[j].green = (rgb >> 8) & 255; // red
rgbData[j].blue = (rgb >> 16) & 255; // blue
int NLIGHTS=4;
i=j;
for(int k=1;k<NLIGHTS;k++) {
i=(i+WS2812_LED_STRIP_LENGTH/NLIGHTS)%WS2812_LED_STRIP_LENGTH;
rgbData[i].red = (rgb & 255); // green
rgbData[i].green = (rgb >> 8) & 255; // red
rgbData[i].blue = (rgb >> 16) & 255; // blue
}
WS2812UpdateStrip();
sysDelayMs(delay/100);
if((GPIOA->IDR & 1)==1 && btn==0)
dir=-dir;
btn=(GPIOA->IDR & 1);
delay=delay+dch;
if(delay<500 || delay>3000)
dch=-dch;
for(int k=0;k<WS2812_LED_STRIP_LENGTH;k++) {
rgbData[k].red = 0;
rgbData[k].green = 0;
rgbData[k].blue = 0;
}
ReadAccelerometer(AccBuffer);
int x=(int)AccBuffer[0];
int y=(int)AccBuffer[1];
int z=(int)AccBuffer[2];
delay=10000-abs(y)*20;
if(delay<0) delay=0;
if(y<0)
dir=1;
else
dir=-1;
j=(j+dir+WS2812_LED_STRIP_LENGTH)%WS2812_LED_STRIP_LENGTH;
rgb=(rgb<<1) /*^ (rand()&1 & rand()&1) ; */| (rgb>>24);
}
}