#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stm32l0xx_hal.h>
/* ======================= ПИНЫ (по diagram.json) ======================= */
// --- ILI9341 ---
// CS -> D3 -> PB0
// DC -> D2 -> PA12
// RST -> RST платы (не трогаем)
// SCK -> D13 -> PB3 (SPI1_SCK)
// MISO -> D12 -> PB4 (SPI1_MISO)
// MOSI -> D11 -> PB5 (SPI1_MOSI)
#define TFT_CS_PORT GPIOB
#define TFT_CS_PIN GPIO_PIN_0
#define TFT_DC_PORT GPIOA
#define TFT_DC_PIN GPIO_PIN_12
// RST дисплея заведён на RST платы — программно не управляем
#define TFT_USE_RST 0
// --- Потенциометры ---
// POT_X -> A0 -> PA0 (ADC_CHANNEL_0)
// POT_Y -> A1 -> PA1 (ADC_CHANNEL_1)
#define POT_X_PIN GPIO_PIN_0
#define POT_Y_PIN GPIO_PIN_1
#define POT_PORT GPIOA
// --- Кнопка -> D4 -> PB7 ---
#define BUT_PORT GPIOB
#define BUT_PIN GPIO_PIN_7
/* ======================= ПАРАМЕТРЫ ======================= */
#define TEXT "RTU MIREA"
#define TEXT_LEN 9
#define MARGIN 10
#define FRAME_MS 50 // 20 кадров/с
#define DEBOUNCE_MS 25
/* ======================= ХЕНДЛЫ ======================= */
SPI_HandleTypeDef hspi1;
ADC_HandleTypeDef hadc;
UART_HandleTypeDef huart2;
/* ======================= ЦВЕТА (RGB565) ======================= */
#define COLOR_BLACK 0x0000
#define COLOR_WHITE 0xFFFF
#define COLOR_GREEN 0x07E0
#define COLOR_YELLOW 0xFFE0
/* ======================= СОСТОЯНИЕ ======================= */
static int x = 20, y = 20;
static int lx = 20, ly = 20;
static int textSize = 3;
static uint16_t textColor = COLOR_GREEN;
static uint16_t bgColor = COLOR_BLACK;
static bool inverted = false;
static uint8_t dispMode = 0; // 0=green/3, 1=yellow/2, 2=инверсия
static unsigned long lastFrame = 0;
static unsigned long lastBtn = 0;
static bool lastBtnState = true; // true = не нажата (pull-up)
/* ======================= ПРОТОТИПЫ ======================= */
void SystemClock_Config(void);
static void MX_USART2_UART_Init(void);
static void MX_SPI1_Init(void);
static void MX_ADC_Init(void);
static void initGPIO(void);
void Error_Handler(void);
/* ======================= ILI9341 (низкий уровень) ======================= */
static void tft_cs(uint8_t s) { HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, s ? GPIO_PIN_SET : GPIO_PIN_RESET); }
static void tft_dc(uint8_t s) { HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, s ? GPIO_PIN_SET : GPIO_PIN_RESET); }
static void tft_write8(uint8_t b) { HAL_SPI_Transmit(&hspi1, &b, 1, HAL_MAX_DELAY); }
static void tft_write16(uint16_t v)
{
uint8_t buf[2] = { (uint8_t)(v >> 8), (uint8_t)(v & 0xFF) };
HAL_SPI_Transmit(&hspi1, buf, 2, HAL_MAX_DELAY);
}
static void tft_cmd(uint8_t c) { tft_dc(0); tft_cs(0); tft_write8(c); tft_cs(1); }
static void tft_data8(uint8_t d) { tft_dc(1); tft_cs(0); tft_write8(d); tft_cs(1); }
static void tft_data16(uint16_t d) { tft_dc(1); tft_cs(0); tft_write16(d); tft_cs(1); }
static void tft_init(void)
{
// RST дисплея идёт с RST платы — программно не трогаем
HAL_Delay(50);
tft_cmd(0x01); HAL_Delay(5); // SWRESET
tft_cmd(0x11); HAL_Delay(120); // SLPOUT
tft_cmd(0x3A); tft_data8(0x55); // COLMOD: 16 бит
tft_cmd(0x36); tft_data8(0x48); // MADCTL: landscape
tft_cmd(0x29); HAL_Delay(20); // DISPON
}
static void tft_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
{
tft_cmd(0x2A); tft_data16(x0); tft_data16(x1);
tft_cmd(0x2B); tft_data16(y0); tft_data16(y1);
tft_cmd(0x2C);
}
static void tft_fill(uint16_t color)
{
tft_set_window(0, 0, 319, 239);
tft_dc(1); tft_cs(0);
for (uint32_t i = 0; i < 320UL * 240UL; i++) tft_write16(color);
tft_cs(1);
}
static void tft_fill_rect(int x0, int y0, int w, int h, uint16_t color)
{
if (w <= 0 || h <= 0) return;
tft_set_window((uint16_t)x0, (uint16_t)y0,
(uint16_t)(x0 + w - 1), (uint16_t)(y0 + h - 1));
tft_dc(1); tft_cs(0);
for (int i = 0; i < w * h; i++) tft_write16(color);
tft_cs(1);
}
static void tft_draw_rect(int x0, int y0, int w, int h, uint16_t color)
{
tft_fill_rect(x0, y0, w, 1, color);
tft_fill_rect(x0, y0 + h - 1, w, 1, color);
tft_fill_rect(x0, y0, 1, h, color);
tft_fill_rect(x0 + w - 1, y0, 1, h, color);
}
/* ======================= ПРОСТОЙ 5x7 ШРИФТ ======================= */
typedef struct { char c; uint8_t col[5]; } Glyph;
static const Glyph FONT5x7[] = {
{ ' ', {0x00,0x00,0x00,0x00,0x00} },
{ 'A', {0x7E,0x11,0x11,0x11,0x7E} },
{ 'E', {0x7F,0x49,0x49,0x49,0x41} },
{ 'I', {0x41,0x41,0x7F,0x41,0x41} },
{ 'M', {0x7F,0x02,0x0C,0x02,0x7F} },
{ 'R', {0x7F,0x09,0x19,0x29,0x46} },
{ 'T', {0x01,0x01,0x7F,0x01,0x01} },
{ 'U', {0x3F,0x40,0x40,0x40,0x3F} },
};
static const uint8_t *glyph_cols(char ch)
{
for (uint32_t i = 0; i < sizeof(FONT5x7)/sizeof(FONT5x7[0]); i++)
if (FONT5x7[i].c == ch) return FONT5x7[i].col;
return FONT5x7[0].col;
}
static void draw_char(int px, int py, char ch, int size, uint16_t fg, uint16_t bg)
{
const uint8_t *cols = glyph_cols(ch);
for (int cx = 0; cx < 5; cx++) {
uint8_t bits = cols[cx];
for (int ry = 0; ry < 7; ry++) {
uint16_t color = (bits & (1 << ry)) ? fg : bg;
tft_fill_rect(px + cx * size, py + ry * size, size, size, color);
}
}
tft_fill_rect(px + 5 * size, py, size, 7 * size, bg);
}
static void draw_text(int px, int py, const char *s, int size, uint16_t fg, uint16_t bg)
{
while (*s) {
draw_char(px, py, *s, size, fg, bg);
px += 6 * size;
s++;
}
}
static int text_w(int size) { return TEXT_LEN * 6 * size; }
static int text_h(int size) { return 7 * size; }
/* ======================= РАМКА ======================= */
static void draw_frame(int px, int py, uint16_t color)
{
int w = text_w(textSize) + 4;
int h = text_h(textSize) + 4;
tft_draw_rect(px - 2, py - 2, w, h, color);
}
static void erase_frame(int px, int py)
{
draw_frame(px, py, bgColor);
}
/* ======================= GPIO / SPI / ADC ======================= */
static void initGPIO(void)
{
GPIO_InitTypeDef g = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// CS = PB0, DC = PA12 — выходы
HAL_GPIO_WritePin(TFT_CS_PORT, TFT_CS_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(TFT_DC_PORT, TFT_DC_PIN, GPIO_PIN_RESET);
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_HIGH;
g.Pin = TFT_CS_PIN;
HAL_GPIO_Init(TFT_CS_PORT, &g);
g.Pin = TFT_DC_PIN;
HAL_GPIO_Init(TFT_DC_PORT, &g);
// Кнопка -> D4 -> PB7, INPUT с подтяжкой
g.Pin = BUT_PIN;
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_PULLUP;
HAL_GPIO_Init(BUT_PORT, &g);
// Аналоговые входы: A0 -> PA0, A1 -> PA1
g.Pin = POT_X_PIN | POT_Y_PIN;
g.Mode = GPIO_MODE_ANALOG;
g.Pull = GPIO_NOPULL;
HAL_GPIO_Init(POT_PORT, &g);
// SPI1 (ремап на PB3/PB4/PB5)
GPIO_InitTypeDef s = {0};
s.Pin = GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5; // SCK, MISO, MOSI
s.Mode = GPIO_MODE_AF_PP;
s.Pull = GPIO_NOPULL;
s.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
s.Alternate = GPIO_AF0_SPI1;
HAL_GPIO_Init(GPIOB, &s);
}
static void MX_SPI1_Init(void)
{
__HAL_RCC_SPI1_CLK_ENABLE();
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
if (HAL_SPI_Init(&hspi1) != HAL_OK) Error_Handler();
}
static void MX_ADC_Init(void)
{
__HAL_RCC_ADC1_CLK_ENABLE();
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_10B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc.Init.SamplingTime = ADC_SAMPLETIME_79CYCLES_5;
if (HAL_ADC_Init(&hadc) != HAL_OK) Error_Handler();
}
static uint32_t adc_read(uint32_t channel)
{
ADC_ChannelConfTypeDef s = {0};
s.Channel = channel;
s.Rank = ADC_RANK_CHANNEL_NUMBER;
HAL_ADC_ConfigChannel(&hadc, &s);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
uint32_t v = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
return v;
}
/* ======================= ОФОРМЛЕНИЕ ======================= */
static void redraw_all(void)
{
tft_fill(bgColor);
draw_text(x, y, TEXT, textSize, textColor, bgColor);
draw_frame(x, y, COLOR_WHITE);
lx = x; ly = y;
}
static void next_mode(void)
{
dispMode = (dispMode + 1) % 3;
switch (dispMode) {
case 0: textColor = COLOR_GREEN; bgColor = COLOR_BLACK; textSize = 3; inverted = false; break;
case 1: textColor = COLOR_YELLOW; bgColor = COLOR_BLACK; textSize = 2; inverted = false; break;
default: textColor = COLOR_WHITE; bgColor = COLOR_BLACK; textSize = 3; inverted = true; break;
}
int maxX = 320 - text_w(textSize) - MARGIN;
int maxY = 240 - text_h(textSize) - MARGIN;
if (maxX < MARGIN) maxX = MARGIN;
if (maxY < MARGIN) maxY = MARGIN;
if (x < MARGIN) x = MARGIN;
if (y < MARGIN) y = MARGIN;
if (x > maxX) x = maxX;
if (y > maxY) y = maxY;
redraw_all();
}
/* ======================= MAIN ======================= */
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_USART2_UART_Init();
initGPIO();
MX_SPI1_Init();
MX_ADC_Init();
tft_init();
tft_fill(bgColor);
draw_text(x, y, TEXT, textSize, textColor, bgColor);
draw_frame(x, y, COLOR_WHITE);
lx = x; ly = y;
printf("ILI9341 demo start\r\n");
while (1)
{
unsigned long now = HAL_GetTick();
/* ---------- 1) Ограничение 20 к/с ---------- */
if (now - lastFrame < FRAME_MS) { HAL_Delay(1); continue; }
lastFrame = now;
/* ---------- 2) Чтение потенциометров ---------- */
uint32_t rawX = adc_read(ADC_CHANNEL_0); // A0 -> PA0 (X)
uint32_t rawY = adc_read(ADC_CHANNEL_1); // A1 -> PA1 (Y)
int maxX = 320 - text_w(textSize) - MARGIN;
int maxY = 240 - text_h(textSize) - MARGIN;
if (maxX < MARGIN) maxX = MARGIN;
if (maxY < MARGIN) maxY = MARGIN;
int newX = MARGIN + (int)((rawX * (uint32_t)(maxX - MARGIN)) / 1023);
int newY = MARGIN + (int)((rawY * (uint32_t)(maxY - MARGIN)) / 1023);
/* ---------- 3) Кнопка: антидребезг 25 мс ---------- */
bool btn = (HAL_GPIO_ReadPin(BUT_PORT, BUT_PIN) == GPIO_PIN_SET); // true = не нажата
if (!btn && lastBtnState && (now - lastBtn) > DEBOUNCE_MS) {
lastBtn = now;
next_mode();
newX = x; newY = y;
}
lastBtnState = btn;
/* ---------- 4) Перемещение только при изменении ---------- */
if (newX != lx || newY != ly) {
erase_frame(lx, ly);
draw_text(lx, ly, TEXT, textSize, bgColor, bgColor);
draw_text(newX, newY, TEXT, textSize, textColor, bgColor);
draw_frame(newX, newY, COLOR_WHITE);
lx = newX; ly = newY;
x = newX; y = newY;
}
}
}
/* ======================= СИСТЕМНЫЕ ======================= */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_4;
RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler();
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) Error_Handler();
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART2;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) Error_Handler();
}
static void MX_USART2_UART_Init(void)
{
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF4_USART2;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK) Error_Handler();
__HAL_RCC_USART2_CLK_ENABLE();
}
void Error_Handler(void)
{
while (1) { }
}
/* ======================= printf -> UART ======================= */
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
int _write(int file, uint8_t *ptr, int len)
{
switch (file)
{
case STDOUT_FILENO:
case STDERR_FILENO:
HAL_UART_Transmit(&huart2, ptr, len, HAL_MAX_DELAY);
break;
default:
return -1;
}
return len;
}Loading
st-nucleo-l031k6
st-nucleo-l031k6