led_driver.c 5.22 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
/* 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();
}