2026-01-26 2:27 AM - last edited on 2026-01-26 2:29 AM by mƎALLEm
I have an ILI9341 SPI screen and a small self-made library that can display different numbers on the screen. I want to be able to update the screen (display a new number) every time there is an interrupt. I have tested the screen functions independently, and they work properly. But when I call them from the interrupt handler, they fail. The board is STM32F407G-DISC1.
Timer configuration
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 999;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 335900;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
Timer interrupt
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
//HAL_SPI_TransmitReceive_DMA(&hspi1, xyz_addr, xyz_data, 6);
if ((new_step == 1) && (draw_ready == 1)) {
draw_number(steps_count, 12, &hspi2);
}
new_step = 0;
}
Display functions (they use polling to interact with the screen, no interrupts or dma)
#include "display.h"
#define DC_GPIO_PORT GPIOB
#define DC_GPIO_PIN GPIO_PIN_14
#define ILI9341_WIDTH 240
#define ILI9341_HEIGHT 320
//uint8_t DIGITS_CODES[10] = {126, 48, 109, 121, 55, 91, 95, 112, 127, 125};
uint8_t DIGITS_CODES[10] = {
63, // 0 = A B C D E F
6, // 1 = B C
91, // 2 = A B D E G
79, // 3 = A B C D G
102, // 4 = B C F G
109, // 5 = A C D F G
125, // 6 = A C D E F G
7, // 7 = A B C
127, // 8 = A B C D E F G
111 // 9 = A B C D F G
};
void Send_Command(uint8_t command, uint8_t *args, int num_args, SPI_HandleTypeDef *hspi) {
HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_RESET);
uint8_t cmd[1] = {command};
HAL_SPI_Transmit(hspi, cmd, 1, 100);
HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_SET);
HAL_SPI_Transmit(hspi, args, num_args, 100);
HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_SET);
}
void Send_Command_NoArgs(uint8_t command, SPI_HandleTypeDef *hspi) {
HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_RESET);
uint8_t cmd[1] = {command};
HAL_SPI_Transmit(hspi, cmd, 1, 100);
HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_SET);
}
void Send_Command_NoArgs_No_NSS_Set(uint8_t command, SPI_HandleTypeDef *hspi) {
HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_RESET);
uint8_t cmd[1] = {command};
HAL_SPI_Transmit(hspi, cmd, 1, 100);
//HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_SET);
}
uint32_t min(uint32_t a, uint32_t b) {
if (a < b) {
return a;
}
return b;
}
void Write_Rectangle(uint16_t row_lower, uint16_t row_higher, uint16_t col_lower, uint16_t col_higher, uint16_t color, SPI_HandleTypeDef *hspi2) {
//16 bit/pixel color order (R:5-bit, G:6-bit, B:5-bit)
//1.Set column
uint8_t col_params[4] = {(uint8_t)(col_lower >> 8), (uint8_t)(col_lower & 0x00FF),
(uint8_t)(col_higher >> 8), (uint8_t)(col_higher & 0x00FF)};
Send_Command(SET_COL_CMD, col_params, 4, hspi2);
//2.Set row
uint8_t row_params[4] = {(uint8_t)(row_lower >> 8), (uint8_t)(row_lower & 0x00FF),
(uint8_t)(row_higher >> 8), (uint8_t)(row_higher & 0x00FF)};
Send_Command(SET_ROW_CMD, row_params, 4, hspi2);
//3.Set color
Send_Command_NoArgs_No_NSS_Set(SET_COLOR_CMD, hspi2);
// Compute rectangle size
uint32_t width = col_higher - col_lower + 1;
uint32_t height = row_higher - row_lower + 1;
uint32_t pixels = width * height;
// Send color repeatedly
HAL_GPIO_WritePin(DC_GPIO_PORT, DC_GPIO_PIN, GPIO_PIN_SET);
int array_size = min(64, pixels * 2);
uint8_t pix_array[64];
for (int i = 0; i < array_size; i++) {
if (i % 2 == 0) {
pix_array[i] = (uint8_t)(color >> 8);
}
else {
pix_array[i] = (uint8_t)(color & 0xFF);
}
}
if (array_size < 64) {
HAL_SPI_Transmit(hspi2, pix_array, array_size, 100);
}
else {
uint32_t pixels_written = 0;
while (pixels_written + array_size < pixels * 2) {
HAL_SPI_Transmit(hspi2, pix_array, array_size, 100);
pixels_written += array_size;
}
HAL_SPI_Transmit(hspi2, pix_array, pixels * 2 - pixels_written, 100);
}
HAL_GPIO_WritePin(NSS_GPIO_PORT, NSS_GPIO_PIN, GPIO_PIN_SET);
}
void draw_digit(uint16_t x, uint16_t y, uint32_t size, uint8_t digit, uint16_t color, SPI_HandleTypeDef *hspi2) {
if (digit > 9) {
return;
}
uint8_t digit_code = DIGITS_CODES[digit];
uint8_t width = size / 6;
for (int i = 0; i < 7; i++) {
if (digit_code & 1) {
switch (i) {
case 0: // A (top)
Write_Rectangle(
y,
y + width,
x,
x + size,
color, hspi2
);
break;
case 1: // B (top right)
Write_Rectangle(
y,
y + size,
x + size,
x + size + width,
color, hspi2
);
break;
case 2: // C (bottom right)
Write_Rectangle(
y + size,
y + size * 2,
x + size,
x + size + width,
color, hspi2
);
break;
case 3: // D (bottom)
Write_Rectangle(
y + size * 2 - width,
y + size * 2,
x,
x + size,
color, hspi2
);
break;
case 4: // E (bottom left)
Write_Rectangle(
y + size,
y + size * 2,
x,
x + width,
color, hspi2
);
break;
case 5: // F (top left)
Write_Rectangle(
y,
y + size,
x,
x + width,
color, hspi2
);
break;
case 6: // G (middle)
Write_Rectangle(
y + size - width / 2,
y + size + width - width / 2,
x,
x + size,
color, hspi2
);
break;
}
}
digit_code = digit_code >> 1;
}
}
void draw_number(uint32_t number, uint32_t size, SPI_HandleTypeDef *hspi2) {
// Find highest power of 10
uint32_t value = number;
uint32_t div = 1;
uint16_t x = 5;
uint16_t y = 5;
uint16_t color = 0;
while (value / div >= 10) {
div *= 10;
}
while (div != 0) {
uint8_t digit = (uint8_t)(value / div);
draw_digit(x, y, size, digit, color, hspi2);
value %= div;
div /= 10;
x += size + 5;
}
}
//actual
///https://github.com/martnak/STM32-ILI9341/blob/master/Src/ILI9341/ILI9341_STM32_Driver.c
void ILI9341_Init(SPI_HandleTypeDef *hspi2)
{
// Hardware reset
HAL_GPIO_WritePin(RST_GPIO_PORT, RST_GPIO_PIN, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(RST_GPIO_PORT, RST_GPIO_PIN, GPIO_PIN_SET);
HAL_Delay(120);
// Software reset
Send_Command_NoArgs(0x01, hspi2);
HAL_Delay(1000);
// POWER CONTROL A
uint8_t pwrA[5] = {0x39, 0x2C, 0x00, 0x34, 0x02};
Send_Command(0xCB, pwrA, 5, hspi2);
// POWER CONTROL B
uint8_t pwrB[3] = {0x00, 0xC1, 0x30};
Send_Command(0xCF, pwrB, 3, hspi2);
// DRIVER TIMING CONTROL A
uint8_t dtca[3] = {0x85, 0x00, 0x78};
Send_Command(0xE8, dtca, 3, hspi2);
// DRIVER TIMING CONTROL B
uint8_t dtcb[2] = {0x00, 0x00};
Send_Command(0xEA, dtcb, 2, hspi2);
// POWER ON SEQUENCE CONTROL
uint8_t pwrSeq[4] = {0x64, 0x03, 0x12, 0x81};
Send_Command(0xED, pwrSeq, 4, hspi2);
// PUMP RATIO CONTROL
uint8_t pump[1] = {0x20};
Send_Command(0xF7, pump, 1, hspi2);
// POWER CONTROL 1 (VRH)
uint8_t pwr1[1] = {0x23};
Send_Command(0xC0, pwr1, 1, hspi2);
// POWER CONTROL 2 (SAP, BT)
uint8_t pwr2[1] = {0x10};
Send_Command(0xC1, pwr2, 1, hspi2);
// VCOM CONTROL 1
uint8_t vcom1[2] = {0x3E, 0x28};
Send_Command(0xC5, vcom1, 2, hspi2);
// VCOM CONTROL 2
uint8_t vcom2[1] = {0x86};
Send_Command(0xC7, vcom2, 1, hspi2);
// MEMORY ACCESS CONTROL (rotation)
uint8_t mac[1] = {0x48};
Send_Command(0x36, mac, 1, hspi2);
// PIXEL FORMAT (RGB565)
uint8_t pixel_fmt[1] = {0x55};
Send_Command(0x3A, pixel_fmt, 1, hspi2);
// FRAME RATE CONTROL
uint8_t frmctr[2] = {0x00, 0x18};
Send_Command(0xB1, frmctr, 2, hspi2);
// DISPLAY FUNCTION CONTROL
uint8_t dfc[3] = {0x08, 0x82, 0x27};
Send_Command(0xB6, dfc, 3, hspi2);
// 3GAMMA FUNCTION DISABLE
uint8_t gamma_disable[1] = {0x00};
Send_Command(0xF2, gamma_disable, 1, hspi2);
// GAMMA CURVE SELECT
uint8_t gamma_curve[1] = {0x01};
Send_Command(0x26, gamma_curve, 1, hspi2);
// POSITIVE GAMMA CORRECTION
uint8_t pgamma[15] = {
0x0F, 0x31, 0x2B, 0x0C, 0x0E,
0x08, 0x4E, 0xF1, 0x37, 0x07,
0x10, 0x03, 0x0E, 0x09, 0x00
};
Send_Command(0xE0, pgamma, 15, hspi2);
// NEGATIVE GAMMA CORRECTION
uint8_t ngamma[15] = {
0x00, 0x0E, 0x14, 0x03, 0x11,
0x07, 0x31, 0xC1, 0x48, 0x08,
0x0F, 0x0C, 0x31, 0x36, 0x0F
};
Send_Command(0xE1, ngamma, 15, hspi2);
// EXIT SLEEP
Send_Command_NoArgs(0x11, hspi2);
HAL_Delay(120);
// DISPLAY ON
Send_Command_NoArgs(0x29, hspi2);
}
void clean_screen(uint16_t color, SPI_HandleTypeDef *hspi2) {
Write_Rectangle(
0, // row_lower
ILI9341_HEIGHT - 1, // row_higher
0, // col_lower
ILI9341_WIDTH - 1, // col_higher
color,
hspi2
);
}
The main
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_I2S3_Init();
MX_SPI1_Init();
MX_USB_HOST_Init();
MX_SPI2_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
//Reset the screen
ILI9341_Init(&hspi2);
//screen experiment, delete later (black rectangle)
HAL_Delay(1000);
//Write_Rectangle(0, 50, 0, 50, 0, &hspi2); //good
//draw_digit(10, 10, 12, 8, 0, &hspi2); //good
//draw_number(UINT32_MAX, 12, &hspi2); //good
draw_ready = 1;
//Set everything for the accelometer
uint8_t OUT_X_L = 0x28;
uint8_t OUT_X_H = 0x29;
uint8_t OUT_Y_L = 0x2A;
uint8_t OUT_Y_H = 0x2B;
uint8_t OUT_Z_L = 0x2C;
uint8_t OUT_Z_H = 0x2D;
uint8_t CTRL_REG5 = 0x24; //for sensitivity (change from default 2 to 4/8) (bits 5-4)
//receivers and transmitters
xyz_addr[0] = OUT_X_L; xyz_addr[1] = OUT_X_H; xyz_addr[2] = OUT_Y_L; xyz_addr[3] = OUT_Y_H; xyz_addr[4] = OUT_Z_L; xyz_addr[5] = OUT_Z_H;
uint8_t ctrl_val;
//fill buffer with 0s
for (int i = 0; i < ACC_BUF_SIZE; i++) {
acc_values_buf[i] = 0;
}
//receive and set accelerator sensitivity
//HAL_SPI_TransmitReceive(&hspi1, &CTRL_REG5, &ctrl_val, 1, 100);
//ctrl_val = ctrl_val | 0x08; //set FSCALE0
//uint8_t tx_buf[2];
//tx_buf[0] = CTRL_REG5 & 0x7F; //register address
//tx_buf[1] = ctrl_val; //data to write into it
//HAL_SPI_Transmit(&hspi1, tx_buf, 1, 100);
//initial dma call (deprecated)
//HAL_SPI_TransmitReceive_DMA(&hspi1, xyz_addr, xyz_data, 6);
//default value
//clean_screen(0xFF, &hspi2);
//draw_number(steps_count, 12, &hspi2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
MX_USB_HOST_Process();
/* USER CODE BEGIN 3 */
//During each SPI clock cycle, full-duplex transmission of a single bit occurs.
//1 clock cycle is 168/32MHz = 5.25MHz
//meaning there are 5.25 * 10^6 bit, or 5.25 * 10^6/8 bytes = 656250 bytes
//or, all coordinates are received 109375 times.
}
/* USER CODE END 3 */
}
2026-01-26 2:55 AM
Hello,
What do you mean by they fail here: "But when I call them from the interrupt handler, they fail".
You need to describe the symptoms: it doesn't display anything? it diplays rubbish characters?
What did you do to debug the issue apart from calling the display APIs outside the Timer interrupt callback?
Meanwhile, draw_number(steps_count, 12, &hspi2); should complete its execution in less time than the timer period.