cancel
Showing results for 
Search instead for 
Did you mean: 

Driving LED matrix with GPIO

artrasa
Associate

Hello all

I am experimenting with driving a 4*8 LED matrix with a Nucleo STM32-F410RB board. I am trying to save GPIO pins by driving the LEDs as a matrix. The schematic and example is attached below.

I'm not very experienced with PWM generation so I used the basic timer TIM6 interrupt for driving the matrix. The setup seems to work, but at low brightness settings the LEDs tend to flicker a bit. My question is: are there some better/more effective ways to achieve a stable LED matrix output? Any suggestions are welcome.

Thanks in advance!LED matrix schematicLED matrix schematicExampleExample

 

5 REPLIES 5
Danish1
Lead III

Your circuit has only one resistor to set current through the LEDs, R101. Because of that, I assume that you only light one LED at a time.

Lighting them one-at-a-time is likely to make the LEDs flickery as you need to cycle through each one so there are many steps.

A better approach is to light them one row at a time but then you would need to put current-setting resistors on each of Q105-Q112, preferably between the collector and its column-of-LEDs

I don’t like the way you are driving the transistors. You show bipolar transistors, which are current-controlled so you should have resistors (say 1k to 10k) between the base of each transistor and the GPIO pin you use to drive it. An alternative would be to use MOSFETs or “digital transistors” that have built-in resistors.  Edit: I just saw those resistors. Sorry if anyone was misled.

Thanks for pointing out the confusing resistor placement.

The point was to light only one LED at a time because I wanted to achieve some form of individual LED brightness control. The brightness of each LED can be adjusted in 10 % increments.

I found I could improve the display smoothness by adjusting the clock settings. With following settings the flicker is minimal:

  • AHB Prescaler: /4
  • HCLK (MHz): 16
  • APB1 Prescaler: /2
  • APB1 Peripheral clocks (MHz): 8
  • APB1 Timer clocks (MHz): 16

Below is the user code I wrote in STMCubeIDE:

 

/* USER CODE BEGIN PFP */

void led_task(void);
void drive_anode(int32_t num);
void drive_cathode(int32_t num);
void clear_cathodes(void);
void clear_anodes(void);

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* 0: led off, 1: led on */
uint8_t led_state[32+1] = { 0,
    1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1
};

/* led brightness level from 0 to 10 */
uint8_t led_brightness[32+1] = { 0,
	 1,  2,  3,  4,  5,  6,  7,  8,
	 9, 10,  9,  8,  7,  6,  5,  4,
	 3,  2,  1,  2,  3,  4,  5,  6,
	 7,  8,  9, 10,  9,  8,  7,  6
};

GPIO_TypeDef * leda_port[4+1] = { 0,
	LEDA0_GPIO_Port, LEDA1_GPIO_Port,
	LEDA2_GPIO_Port, LEDA3_GPIO_Port
};
uint16_t leda_pin[4+1] = { 0,
	LEDA0_Pin, LEDA1_Pin,
	LEDA2_Pin, LEDA3_Pin
};
GPIO_TypeDef * ledc_port[8+1] = { 0,
	LEDC0_GPIO_Port, LEDC1_GPIO_Port, LEDC2_GPIO_Port, LEDC3_GPIO_Port,
	LEDC4_GPIO_Port, LEDC5_GPIO_Port, LEDC6_GPIO_Port, LEDC7_GPIO_Port
};
uint16_t ledc_pin[8+1] = { 0,
	LEDC0_Pin, LEDC1_Pin, LEDC2_Pin, LEDC3_Pin,
	LEDC4_Pin, LEDC5_Pin, LEDC6_Pin, LEDC7_Pin
};

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_TIM6_Init();
  /* USER CODE BEGIN 2 */
  int32_t count = 0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE BEGIN 3 */
	if (tim6_tick == 1)
	{
		tim6_tick = 0;
		led_task();
		++count;
	}

	if (count >= 4000)
	{
		count = 0;

		uint8_t tmp = led_brightness[32];
		for (int32_t i = 32; i >= 1; i--)
		{
			led_brightness[i] = led_brightness[i - 1];
		}
		led_brightness[1] = tmp;
	}
  }
  /* USER CODE END 3 */
}

/* USER CODE BEGIN 4 */

void led_task(void)
{
	static int32_t n_cathode = 1;
	static int32_t n_anode = 1;
	static int32_t n_cycles = 0;
	const int8_t max_cycles = 10;
	int8_t duty;
	int32_t num_led;

	/* which led */
	num_led = (n_anode - 1) * 8 + n_cathode;

	/* duty cycle */
	duty = led_brightness[num_led];

	if ( (led_state[num_led] > 0) && (n_cycles < duty) ) {
		drive_anode(n_anode);
		drive_cathode(n_cathode);
	}
	else
	{
		clear_anodes();
//		clear_cathodes();
	}

	++n_cycles;
	if (n_cycles >= max_cycles)
	{
		n_cycles = 0;

		clear_anodes();
		clear_cathodes();

		++n_cathode;
		if (n_cathode > 8)
		{
			n_cathode = 1;

			++n_anode;
			if (n_anode > 4)
			{
				n_anode = 1;
			}
		}
	}
}

void drive_anode(int32_t num)
{
	HAL_GPIO_WritePin(leda_port[num], leda_pin[num], GPIO_PIN_RESET);
}

void clear_anodes(void)
{
	for (int32_t i = 1; i <= 4; i++)
	{
		HAL_GPIO_WritePin(leda_port[i], leda_pin[i], GPIO_PIN_SET);
	}
}

void drive_cathode(int32_t num)
{
	HAL_GPIO_WritePin(ledc_port[num], ledc_pin[num], GPIO_PIN_SET);
}

void clear_cathodes(void)
{
	for (int32_t i = 1; i <= 8; i++)
	{
		HAL_GPIO_WritePin(ledc_port[i], ledc_pin[i], GPIO_PIN_RESET);
	}
}

/* USER CODE END 4 */

 

 

 

gbm
Lead III

No need for cathode transistors at all. Skip them and adjust the resistor values for cathode driving - you definitely need one resistor per driven LED. Remove (short) R101. You may easily regulate LED brightness for 7 LEDs in a row in approx. 50 steps using timer interrupt and software-driven PWM generation.

Assuming 400 Hz refresh, 1600 Hz row switch frequency, 50 steps give 80 kHz timer interrupt frequency for pure software PWM - not a big problem for F4 running at 80..96 MHz provided you don't use HAL.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

Hi @gbm hope you don't mind me disagreeing with my understanding of what you wrote. I could have misunderstood what you wrote in which case please correct me.

"No need for cathode transistors" - What current do you expect to flow through the LEDs? I've checked the data sheet of stm32f4 and it shows an "absolute maximum" of 25 mA per I/O pin. When driving LEDs at low duty-cycle I regularly exceed 20 mA instantaneous current to get decent brightness so I think transistors top and bottom is good. You might say that with 68 ohms the LED current will not exceed that, but who is to know that the OP won't want to drive them brighter in future.

"One resistor per driven LED" - Are you saying OP needs 32 resistors? Or do you actually mean one per column?

gbm
Lead III

If you need to drive LEDs at 20 mA, you must have really old LEDs. ;) For indoor displays, I usually use up to 5 mA for 4-way multiplexing.

One resistor per column of course.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice