16x16 square of neopixels for stm32f3 discovery board

Dependencies:   STM32F3-Discovery-minimal

Fork of neopixels by Martin Johnson

neopixel.c

Committer:
MartinJohnson
Date:
2016-05-25
Revision:
0:d89511b21e3d
Child:
1:6ed7aa3c8efa

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);

    }
}