Skip to content
led_driver.c 5.22 KiB
Newer Older
laurentc's avatar
laurentc committed
/* Includes ------------------------------------------------------------------*/
#include "stm32f3xx_hal.h"
#include "globals.h"
/* USER CODE BEGIN Includes */
#include "led_driver.h"
/* USER CODE END Includes */

// Format Image data so that it can be pushed as is by DMA toward the GPIO
// Starting from image in raw format RGB.
// lines from left to right and top to bottom

// NUCLEO 64 avec 446
// to be put in port B0..10,12..15 
// and PC0..PC4


// Sequence de pilotage des leds
// 8 bits G7..0, 8 bits R7..0, 8 bits B7..0

// Encodage des bits
// Bit 0 : 100
// Bit 1 : 110


// To be called just once
// This is actually supposed to be done at the end, but that's ok this way
void initLedResetCodeInMemoryForDMA2(uint8_t *portamem)
{
	uint32_t i;
	
	// pre fill never changing parts
	for (i=0;i<NB_LEDS_PER_STRIP*8*3;i++)
	{
		*portamem++=0xFF;// pre fill, never changes
		*portamem++=0x00;// will be overwritten
		*portamem++=0x00;// pre fill, never changes
	}

	for (i=0;i<LED_RESET_DELAY;i++)
	{
		*portamem++ = 0;
	}
	
}

uint8_t btable[32] = 
{
  0,1,2,3,4,5,6,7,
  0,1,2,3,4,5,6,7,
  0,1,2,3,4,5,6,7,
  0,1,2,3,4,5,6,7
};

uint16_t bbintable[32];

// 0 for PORTA,
// 1 for PORTB, 
// 2 for PORTC
// 3 for PORTD


uint8_t ptable[32] = 
{
	0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,
};


// 0 <= integer_offset < NB_LEDS_PER_STRIP required
// Version rerouting bits
// Une part de la compxit de ce code vient de ce qu'il ralise un offset entier integer_offset horizontal sur l'image, il doit grer le rebouclage image

// R,G,B,R,G,B,R,G,B : raw image 
// G,R,B,G,R,B,G,R,B : how it must be sent to WS2812B
const int8_t rgbtrip[3] = {-1,2,2}; // starts with G then R (-1) then B (+2) then again +2 to reach next G 

__attribute__((section("ccmram")))

void formatMemoryForDMA2(uint8_t *raw, uint8_t *portamem, uint8_t integer_offset)
{
		uint8_t line,column,rgb_bit;
		uint8_t *rgb[NB_LINES];
		uint8_t tmpmem[4];
		uint8_t rgbsequence;
	  static uint8_t initialized = 0;

		if (initialized==0)
		{
			for (line=0;line<32;line++)
			{
				bbintable[line] = 1 << btable[line];
			}
			initialized = 1;
		}

		// calcul pour chaque ligne l'adresse de dbut
		for (line=0;line<NB_LINES;line++)
		{
			rgb[line] = raw+1+integer_offset*3; // Starts with G, hence the +1
			// Go to next Line
			raw+=NB_LEDS_PER_STRIP*3;
		}

		portamem++; // skip first pre filled =0xFFFF;
		
		for (column=0;column<NB_LEDS_PER_STRIP;column++)
		{
			// avec l'integer_offset, si on arrive au bout de la bande, il faut revenir au dbut
			// seulement une fois. 
			// si integer_offset==1, a se fait juste  la dernire colonne : column==NB_LEDS_PER_STRIP-1
			if (column+integer_offset==NB_LEDS_PER_STRIP)
			{
				for (line=0;line<NB_LINES;line++)
				{
					rgb[line]-=NB_LEDS_PER_STRIP*3; 
				}				
			}			
			
			// Send the 3 colors for the column
			for (rgbsequence=0;rgbsequence<3;rgbsequence++)
			{
				// 3x G then R then B
				rgb_bit = 0x80;
				while(rgb_bit)
				{
					// PORTB&C
//					portamem++; // skip =0xFFFF;, already done
//					portcmem++; // skip =0xFFFF;, already done
					tmpmem[0] = 0;
					tmpmem[1] = 0;
					// PORTB&C 0..NB_LINES
					for (line=0;line<NB_LINES;line++)
					{
						if ((*rgb[line])&rgb_bit)
						{
							tmpmem[ptable[line]] |= bbintable[line]; // routing according to printed circuit board
						}
					}
					*portamem = tmpmem[0];
					portamem+=3; // skip =0x0000, 0xFFFF;
					rgb_bit = rgb_bit >> 1; // Bit 7  Bit 0.
				}
			
				for (line=0;line<NB_LINES;line++)
				{
					rgb[line]+=rgbtrip[rgbsequence]; // next is R, next time +=2, then +=2
				}
			}
			
		}
		
}


extern DMA_HandleTypeDef hdma_tim2_ch1;
extern TIM_HandleTypeDef htim2;

void dmaLedDrive2(uint8_t *portamem)
{

	GPIOA->ODR = 0;
	
	HAL_DMA_Start(&hdma_tim2_ch1, (uint32_t) portamem, (uint32_t) &GPIOA->ODR, NB_DMA_WRITES);

	// pourrait s'avrer ncessaire d'interrompre les interrupions entre ces deux instructions pour viter un dphasage.
	// pas de problme, rien ne se passe avant que le timer 8 ne soit dmarr.
  __HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1);


// Appelle pas ces fonction, pour ne pas dmarrer le timer avant la fin de la config
//	HAL_TIM_OC_Start(&htim8,TIM_CHANNEL_1);  //Dmarre aussi le Timer 8
//	HAL_TIM_OC_Start(&htim8,TIM_CHANNEL_3);
// Ici on n'a qu'un seul channel, alors, c'est OK aussi 
	
  TIM_CCxChannelCmd(TIM2, TIM_CHANNEL_1, TIM_CCx_ENABLE);
  
  if(IS_TIM_BREAK_INSTANCE(TIM2) != RESET)  
  {
    /* Enable the main output */
    __HAL_TIM_MOE_ENABLE(&htim2);
  }
	
  /* Enable the Peripheral */
  __HAL_TIM_ENABLE(&htim2); 
  

}


void dmaPostLedDrive(void)
{
	// Il faut dsactiver le CC0 avant le timer lui mme. Sinon, __HAL_TIM_DISABLE est non fonctionnel.
  TIM_CCxChannelCmd(TIM2, TIM_CHANNEL_1, TIM_CCx_DISABLE);
	__HAL_TIM_MOE_DISABLE(&htim2);
	__HAL_TIM_DISABLE(&htim2); 
  __HAL_TIM_DISABLE_DMA(&htim2, TIM_DMA_CC1);
}

void dmaWait(void)
{
	HAL_StatusTypeDef dma_state;

	dma_state =  HAL_DMA_PollForTransfer(&hdma_tim2_ch1, HAL_DMA_FULL_TRANSFER, 20);
	if (dma_state == HAL_OK)
	{
			dmaPostLedDrive(); // 1s ou moins
	}				
	else
		crash(CC_DMA_TIM2_CH1_FAILED);
}

// Only one case  : in case of loss of power
void dmaUrgentStop(void)
{
	__HAL_DMA_DISABLE(&hdma_tim2_ch1);
	HAL_DMA_Abort(&hdma_tim2_ch1);
//  dmaPostLedDrive();
}