//===================================================================
// ПОЛНЫЙ ТЕСТ ДИСПЛЕЯ ILI9341 С СИНУСОИДОЙ ДЛЯ BLUE PILL
// Всё в одном файле для онлайн симулятора
//===================================================================
// Адреса периферии
#define RCC_BASE 0x40021000
#define GPIOA_BASE 0x40010800
#define GPIOB_BASE 0x40010C00
#define SPI1_BASE 0x40013000
// Регистры RCC
#define RCC_APB2ENR (*(volatile unsigned long*)(RCC_BASE + 0x18))
#define RCC_APB2ENR_IOPAEN (1 << 2)
#define RCC_APB2ENR_IOPBEN (1 << 3)
#define RCC_APB2ENR_SPI1EN (1 << 12)
// Регистры GPIOA
#define GPIOA_CRL (*(volatile unsigned long*)(GPIOA_BASE + 0x00))
#define GPIOA_BSRR (*(volatile unsigned long*)(GPIOA_BASE + 0x10))
// Регистры GPIOB
#define GPIOB_CRL (*(volatile unsigned long*)(GPIOB_BASE + 0x00))
#define GPIOB_CRH (*(volatile unsigned long*)(GPIOB_BASE + 0x04))
#define GPIOB_BSRR (*(volatile unsigned long*)(GPIOB_BASE + 0x10))
// Регистры SPI1
#define SPI1_CR1 (*(volatile unsigned long*)(SPI1_BASE + 0x00))
#define SPI1_SR (*(volatile unsigned long*)(SPI1_BASE + 0x08))
#define SPI1_DR (*(volatile unsigned long*)(SPI1_BASE + 0x0C))
// Биты SPI
#define SPI_CR1_MSTR (1 << 2)
#define SPI_CR1_BR (2 << 3) // fPCLK/8
#define SPI_CR1_SSI (1 << 8)
#define SPI_CR1_SSM (1 << 9)
#define SPI_CR1_SPE (1 << 6)
#define SPI_SR_TXE (1 << 1)
#define SPI_SR_BSY (1 << 7)
// Пины дисплея ILI9341
#define PIN_SCLK 5 // PA5
#define PIN_MOSI 7 // PA7
#define PIN_RES 0 // PB0
#define PIN_DC 1 // PB1
#define PIN_CS 10 // PB10
// Размеры экрана
#define SCREEN_WIDTH 240
#define SCREEN_HEIGHT 320
// Цвета (RGB565)
#define BLACK 0x0000
#define RED 0xF800
#define GREEN 0x07E0
#define BLUE 0x001F
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define GRAY 0x8410 // Серый для осей
#define DARKGRAY 0x3186 // Темно-серый для сетки
// Макросы для управления пинами
#define CS_LOW() (GPIOB_BSRR = (1 << (PIN_CS + 16)))
#define CS_HIGH() (GPIOB_BSRR = (1 << PIN_CS))
#define DC_LOW() (GPIOB_BSRR = (1 << (PIN_DC + 16)))
#define DC_HIGH() (GPIOB_BSRR = (1 << PIN_DC))
#define RES_LOW() (GPIOB_BSRR = (1 << (PIN_RES + 16)))
#define RES_HIGH() (GPIOB_BSRR = (1 << PIN_RES))
// Константы для математики (свои, без math.h)
#define PI 3.14159
// Простая задержка
void myDelay(volatile unsigned int count) {
while(count--) {
__asm__("nop");
}
}
// Быстрое возведение в квадрат
int square(int x) {
return x * x;
}
// Простая функция синуса через ряд Тейлора (для -PI до PI)
float mySin(float x) {
// Приводим x к диапазону -PI до PI
while(x > PI) x -= 2*PI;
while(x < -PI) x += 2*PI;
// Ряд Тейлора: sin(x) = x - x^3/6 + x^5/120 - x^7/5040
float x2 = x * x;
float x3 = x2 * x;
float x5 = x3 * x2;
float x7 = x5 * x2;
return x - x3/6.0 + x5/120.0 - x7/5040.0;
}
// Простая функция экспоненты (приближенно)
float myExp(float x) {
float result = 1.0;
float term = 1.0;
// Ряд Тейлора для e^x
for(int i = 1; i < 8; i++) {
term *= x / i;
result += term;
}
return result;
}
// Отправка байта по SPI
void spi_send(uint8_t data) {
while(!(SPI1_SR & SPI_SR_TXE));
SPI1_DR = data;
while(!(SPI1_SR & SPI_SR_TXE));
while(SPI1_SR & SPI_SR_BSY);
}
// Отправка команды дисплею
void send_cmd(uint8_t cmd) {
DC_LOW();
CS_LOW();
spi_send(cmd);
CS_HIGH();
}
// Отправка данных дисплею
void send_data(uint8_t data) {
DC_HIGH();
CS_LOW();
spi_send(data);
CS_HIGH();
}
// Отправка 16-битного цвета
void send_color(uint16_t color) {
DC_HIGH();
CS_LOW();
spi_send(color >> 8); // Старший байт
spi_send(color & 0xFF); // Младший байт
CS_HIGH();
}
// Установка окна для заливки
void set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
send_cmd(0x2A); // Column Address Set
send_data(x0 >> 8);
send_data(x0);
send_data(x1 >> 8);
send_data(x1);
send_cmd(0x2B); // Page Address Set
send_data(y0 >> 8);
send_data(y0);
send_data(y1 >> 8);
send_data(y1);
send_cmd(0x2C); // Memory Write
}
// Заливка всего экрана цветом
void fill_screen(uint16_t color) {
set_window(0, 0, 239, 319); // ILI9341 240x320
DC_HIGH();
CS_LOW();
// Отправляем цвет для каждого пикселя
for(int i = 0; i < 240 * 320; i++) {
while(!(SPI1_SR & SPI_SR_TXE));
SPI1_DR = color >> 8;
while(!(SPI1_SR & SPI_SR_TXE));
SPI1_DR = color & 0xFF;
}
while(SPI1_SR & SPI_SR_BSY);
CS_HIGH();
}
// Инициализация SPI
void spi_init(void) {
// Включаем тактирование
RCC_APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_SPI1EN;
myDelay(1000);
// Настройка PA5 (SCLK) как альтернативная функция push-pull
GPIOA_CRL &= ~(0xF << 20); // Очистка битов для PA5
GPIOA_CRL |= (0xB << 20); // 1011 = Alternate function push-pull 50MHz
// Настройка PA7 (MOSI) как альтернативная функция push-pull
GPIOA_CRL &= ~(0xF << 28); // Очистка битов для PA7
GPIOA_CRL |= (0xB << 28); // 1011 = Alternate function push-pull 50MHz
// Настройка PB0 (RES), PB1 (DC), PB10 (CS) как выходы
// PB0 (CRL биты 0-3)
GPIOB_CRL &= ~0xF;
GPIOB_CRL |= 0x3; // 0011 = Push-pull output 50MHz
// PB1 (CRL биты 4-7)
GPIOB_CRL &= ~(0xF << 4);
GPIOB_CRL |= (0x3 << 4);
// PB10 (CRH биты 8-11)
GPIOB_CRH &= ~(0xF << 8);
GPIOB_CRH |= (0x3 << 8);
// Установка начального состояния
CS_HIGH();
DC_HIGH();
RES_HIGH();
// Настройка SPI
SPI1_CR1 = SPI_CR1_MSTR | SPI_CR1_BR | SPI_CR1_SSI | SPI_CR1_SSM;
SPI1_CR1 |= SPI_CR1_SPE;
}
// Инициализация дисплея ILI9341
void ili9341_init(void) {
// Сброс дисплея
RES_LOW();
myDelay(100000);
RES_HIGH();
myDelay(100000);
send_cmd(0x01); // Software Reset
myDelay(100000);
send_cmd(0x36); // Memory Access Control
send_data(0x48); // Ориентация
send_cmd(0x3A); // Pixel Format Set
send_data(0x55); // 16-bit color
send_cmd(0x11); // Sleep Out
myDelay(100000);
send_cmd(0x29); // Display On
myDelay(10000);
}
// Рисование точки с проверкой границ
void draw_pixel(uint16_t x, uint16_t y, uint16_t color) {
if(x >= SCREEN_WIDTH || y >= SCREEN_HEIGHT) return;
set_window(x, y, x, y);
send_color(color);
}
// Рисование линии (алгоритм Брезенхема)
void draw_line(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) {
int dx = x1 > x0 ? x1 - x0 : x0 - x1;
int dy = y1 > y0 ? y1 - y0 : y0 - y1;
int sx = x0 < x1 ? 1 : -1;
int sy = y0 < y1 ? 1 : -1;
int err = dx - dy;
int e2;
while(1) {
draw_pixel(x0, y0, color);
if(x0 == x1 && y0 == y1) break;
e2 = 2 * err;
if(e2 > -dy) {
err -= dy;
x0 += sx;
}
if(e2 < dx) {
err += dx;
y0 += sy;
}
}
}
// Рисование сетки и осей
void draw_grid(void) {
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT / 2;
// Вертикальные линии сетки
for(int x = 20; x < SCREEN_WIDTH; x += 30) {
if(x != centerX) {
draw_line(x, 0, x, SCREEN_HEIGHT - 1, DARKGRAY);
}
}
// Горизонтальные линии сетки
for(int y = 20; y < SCREEN_HEIGHT; y += 30) {
if(y != centerY) {
draw_line(0, y, SCREEN_WIDTH - 1, y, DARKGRAY);
}
}
// Оси координат (жирные линии)
draw_line(centerX, 0, centerX, SCREEN_HEIGHT - 1, GRAY);
draw_line(0, centerY, SCREEN_WIDTH - 1, centerY, GRAY);
// Стрелочки на осях
draw_line(centerX - 5, 10, centerX, 0, GRAY);
draw_line(centerX + 5, 10, centerX, 0, GRAY);
draw_line(SCREEN_WIDTH - 10, centerY - 5, SCREEN_WIDTH - 1, centerY, GRAY);
draw_line(SCREEN_WIDTH - 10, centerY + 5, SCREEN_WIDTH - 1, centerY, GRAY);
}
// Рисование синусоиды
void draw_sine_wave(void) {
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT / 2;
// Рисуем 3 периода синусоиды
for(int x = 0; x < SCREEN_WIDTH; x++) {
// Отображение x в радианы: от -3*PI до 3*PI
float rad = (x - centerX) * (6 * PI / SCREEN_WIDTH);
// Вычисляем y: centerY + 100 * sin(rad)
float y = centerY + 120 * mySin(rad);
// Рисуем точку с утолщением
if(y >= 0 && y < SCREEN_HEIGHT) {
draw_pixel(x, (int)y, CYAN);
if(y > 0) draw_pixel(x, (int)y - 1, CYAN);
if(y < SCREEN_HEIGHT - 1) draw_pixel(x, (int)y + 1, CYAN);
}
}
// Отмечаем ключевые точки
draw_pixel(centerX, centerY, YELLOW); // Начало координат
// Пики синусоиды
draw_pixel(centerX + 40, centerY - 120, RED);
draw_pixel(centerX - 40, centerY + 120, RED);
draw_pixel(centerX + 120, centerY + 120, RED);
}
// Рисование затухающей синусоиды
void draw_damped_sine(void) {
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT / 2;
for(int x = 0; x < SCREEN_WIDTH; x++) {
float rad = (x - centerX) * (8 * PI / SCREEN_WIDTH);
float damp = myExp(-(float)square(x - centerX) / 5000.0);
float y = centerY + 140 * damp * mySin(rad);
if(y >= 0 && y < SCREEN_HEIGHT) {
draw_pixel(x, (int)y, GREEN);
}
}
}
// Рисование двух синусоид со сдвигом
void draw_phase_shift(void) {
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT / 2;
for(int x = 0; x < SCREEN_WIDTH; x++) {
float rad = (x - centerX) * (4 * PI / SCREEN_WIDTH);
// Синус (голубой)
float y1 = centerY + 100 * mySin(rad);
if(y1 >= 0 && y1 < SCREEN_HEIGHT) {
draw_pixel(x, (int)y1, CYAN);
}
// Косинус (розовый) - со сдвигом на PI/2
float y2 = centerY + 100 * mySin(rad + PI/2);
if(y2 >= 0 && y2 < SCREEN_HEIGHT) {
draw_pixel(x, (int)y2, MAGENTA);
}
}
}
//===================================================================
// ГЛАВНАЯ ФУНКЦИЯ
//===================================================================
int main(void) {
// Инициализация
spi_init();
ili9341_init();
// Основной цикл
while(1) {
// 1. Простая синусоида
fill_screen(BLACK);
draw_grid();
draw_sine_wave();
myDelay(4000000);
// 2. Синус и косинус
fill_screen(BLACK);
draw_grid();
draw_phase_shift();
myDelay(4000000);
// 3. Затухающая синусоида
fill_screen(BLACK);
draw_grid();
draw_damped_sine();
myDelay(4000000);
// 4. Синусоида на цветном фоне
fill_screen(BLUE);
draw_sine_wave();
myDelay(3000000);
// 5. Красный фон с синусоидой
fill_screen(RED);
draw_sine_wave();
myDelay(3000000);
// 6. Зеленый фон с синусоидой
fill_screen(GREEN);
draw_sine_wave();
myDelay(3000000);
}
return 0;
}