STM32 И SPI EEPROM НА ОСНОВЕ M95256-WMN6TP

Не взялся бы заниматься этим осклизлым дрочевом, если бы не приходилось сталкиваться с такой микрухой. Ну, раз уж пришлось, то развернусь тут, пожалуй по-полной. Итак, в этой статье мы рассмотрим способ тестирования энергонезависимой памяти SPI‑EEPROM M95256‑WMN6TP с помощью контроллера STM32F030K6T6 и RS‑485/UART для вывода результатов. Ниже — краткий обзор чипа памяти, её особенностей и пример готовой программы.

Обзор M95256‑WMN6TP

  • Ёмкость: 256 Кбит (32 Кбайта)
  • Интерфейс: SPI с режимами CPOL=1, CPHA=1
  • Напряжение питания: 2.7…3.6 В
  • Точность адресации: 16‑разрядный адрес
  • Страницы записи: 64 байта
  • Максимальная скорость шины: до 10 МГц

Что означают CPOL = 1 и CPHA = 1 в SPI (режим 3), читаем ЗДЕСЬ!

Плюсы EEPROM:

  • Низкое энергопотребление в режиме хранения
  • Простая команда записи/чтения
  • Поддержка «страничной» записи для ускорения передачи больших блоков

Минусы:

  • Ограниченный ресурс циклов программирования (~1 000 000 циклов)
  • Необходимость задержек или чтения статуса для завершения записи
  • Невысокая скорость при последовательной записи, если не использовать страничную арх.

Подключение 

STM32F030K6T6M95256‑WMN6TPНазначение
PA5 (SPI1_SCK) pin 6 (C) Clock
PA7 (SPI1_MOSI) pin 5 (D) Data In
PA6 (SPI1_MISO) pin 2 (Q) Data Out
PA4 (GPIO CS) pin 1 (S) Chip Select
VCC 3.3 В pin 3, 7, 8 VCC, HOLD, WP подтянуты
m32f030k6t6 pinout 1 M95256 WMN6TP

Структура примера кода

  1. Инициализация системного тактирования (8 МГц HSI)
  2. Настройка GPIO для CS, RS‑485, UART
  3. Инициализация UART/RS‑485 для вывода результатов теста
  4. Инициализация SPI1 (Master, CPOL=1, CPHA=1, 8 бит, 500 кГц)
  5. Драйвер M95256:
    • WriteEnable() — разрешение записи
    • GetStatus() — чтение регистра статуса
    • WriteByte/ReadByte() — запись/чтение одного байта
    • WriteBuffer/ReadBuffer() — работа с блоками и страничная привязка
  6. Тестирование памяти:
    • Считываем статус (WEL, WIP)
    • Читаем один байт до записи
    • Записываем строку «TestData» блоком
    • Читаем её обратно и выводим через UART
#include "main.h"
#include <string.h>
#include <stdio.h>
 
#define SYSCLK_HZ 8000000UL   // Тактовая частота системы: 8 МГц
#define BAUDRATE  115200UL    // Скорость UART: 115200 бод
 
// Пин для управления направлением RS485
#define RS485_CONTROL_PIN GPIO_PIN_1
#define RS485_CONTROL_PORT GPIOF
 
// Пин для Chip Select M95256
#define M95256_CS_PIN GPIO_PIN_4
#define M95256_CS_PORT GPIOA
 
// Буфер для приёма данных UART
#define RX_BUFFER_SIZE 64
char rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_index = 0;
 
// Прототипы функций
void SystemClock_Config(void);
void UART_Init(void);
void Timer_Init(void);
void UART_SendString(char *str);
static void MX_GPIO_Init(void);
void RS485_Init(void);
void RS485_SetTransmitMode(void);
void RS485_SetReceiveMode(void);
void SPI_Init(void);
void M95256_Init(void);
void M95256_WriteEnable(void);
uint8_t M95256_GetStatus(void);
void M95256_WriteByte(uint16_t address, uint8_t data);
uint8_t M95256_ReadByte(uint16_t address);
void M95256_WriteBuffer(uint16_t address, uint8_t *data, uint16_t length);
void M95256_ReadBuffer(uint16_t address, uint8_t *buffer, uint16_t length);
 
// Задержка для таймингов (в микросекундах)
void Delay_us(uint32_t us) {
    uint32_t cycles = (SYSCLK_HZ / 1000000UL) * us;
    while (cycles--) {
        __NOP();
    }
}
 
// Инициализация RS485
void RS485_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOF_CLK_ENABLE();
    GPIO_InitStruct.Pin = RS485_CONTROL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(RS485_CONTROL_PORT, &GPIO_InitStruct);
    RS485_SetReceiveMode();
}
 
// Установка режима передачи для RS485
void RS485_SetTransmitMode(void) {
    HAL_GPIO_WritePin(RS485_CONTROL_PORT, RS485_CONTROL_PIN, GPIO_PIN_SET);
}
 
// Установка режима приёма для RS485
void RS485_SetReceiveMode(void) {
    HAL_GPIO_WritePin(RS485_CONTROL_PORT, RS485_CONTROL_PIN, GPIO_PIN_RESET);
}
 
// Инициализация UART
void UART_Init(void) {
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
 
    GPIOA->MODER &= ~(GPIO_MODER_MODER2_Msk | GPIO_MODER_MODER3_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1);
    GPIOA->AFR[0] &= ~((0xF << GPIO_AFRL_AFSEL2_Pos) | (0xF << GPIO_AFRL_AFSEL3_Pos));
    GPIOA->AFR[0] |= ((1 << GPIO_AFRL_AFSEL2_Pos) | (1 << GPIO_AFRL_AFSEL3_Pos));
 
    USART1->CR1 = 0;
    USART1->CR2 = 0;
    USART1->BRR = SYSCLK_HZ / BAUDRATE;
    USART1->CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE);
 
    NVIC_EnableIRQ(USART1_IRQn);
}
 
// Отправка строки через UART
void UART_SendString(char *str) {
    RS485_SetTransmitMode();
    while (*str) {
        while (!(USART1->ISR & USART_ISR_TXE)) {}
        USART1->TDR = *str++;
    }
    while (!(USART1->ISR & USART_ISR_TC)) {}
    RS485_SetReceiveMode();
}
 
// Инициализация таймера TIM3
void Timer_Init(void) {
    __HAL_RCC_TIM3_CLK_ENABLE();
    TIM3->PSC = (SYSCLK_HZ / 1000) - 1;
    TIM3->ARR = 1000 - 1;
    TIM3->DIER |= TIM_DIER_UIE;
    NVIC_EnableIRQ(TIM3_IRQn);
    TIM3->CR1 |= TIM_CR1_CEN;
}
 
// Инициализация SPI
void SPI_Init(void) {
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
 
    // Настройка пинов PA5 (SCK), PA6 (MISO), PA7 (MOSI) для SPI
    GPIOA->MODER &= ~(GPIO_MODER_MODER5_Msk | GPIO_MODER_MODER6_Msk | GPIO_MODER_MODER7_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);
    GPIOA->AFR[0] &= ~((0xF << GPIO_AFRL_AFSEL5_Pos) | (0xF << GPIO_AFRL_AFSEL6_Pos) | (0xF << GPIO_AFRL_AFSEL7_Pos));
    GPIOA->AFR[0] |= ((0 << GPIO_AFRL_AFSEL5_Pos) | (0 << GPIO_AFRL_AFSEL6_Pos) | (0 << GPIO_AFRL_AFSEL7_Pos)); // AF0 для SPI1
 
    // Настройка SPI: Master, CPOL=1, CPHA=1, 8 бит, ~500 кГц
    SPI1->CR1 = 0;
    SPI1->CR1 |= (SPI_CR1_MSTR | SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPOL | SPI_CR1_CPHA); // fPCLK/16
    SPI1->CR2 = 0;
    SPI1->CR2 |= (7 << SPI_CR2_DS_Pos); // 8 бит данных
    SPI1->CR1 |= SPI_CR1_SPE; // Включаем SPI
}
 
// Инициализация M95256
void M95256_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = M95256_CS_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(M95256_CS_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET); // /S высокий
    Delay_us(100); // Задержка для стабилизации после включения
}
 
// Отправка одного байта по SPI с возвратом полученного
uint8_t SPI_Transfer(uint8_t data) {
    while (!(SPI1->SR & SPI_SR_TXE)) {} // Ждём готовности передатчика
    SPI1->DR = data; // Отправляем байт
    while (!(SPI1->SR & SPI_SR_RXNE)) {} // Ждём приёма
    return SPI1->DR; // Возвращаем принятый байт
}
 
// Активация записи
void M95256_WriteEnable(void) {
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET); // /S низкий
    Delay_us(1); // Тайминг tSHSL
    SPI_Transfer(0x06); // Команда WREN
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET); // /S высокий
    Delay_us(1); // Тайминг tSLSH
}
 
// Чтение статуса M95256
uint8_t M95256_GetStatus(void) {
    uint8_t status;
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x05); // Команда RDSR
    status = SPI_Transfer(0xFF); // Фиктивный байт для чтения статуса
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
    return status;
}
 
// Запись одного байта
void M95256_WriteByte(uint16_t address, uint8_t data) {
    M95256_WriteEnable();
    // Проверяем, что WEL=1
    if (!(M95256_GetStatus() & 0x02)) {
        UART_SendString("Error: Write Enable failed\r\n");
        return;
    }
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x02); // Команда WRITE
    SPI_Transfer(address >> 8); // Старший байт адреса
    SPI_Transfer(address & 0xFF); // Младший байт адреса
    SPI_Transfer(data); // Данные
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
    while (M95256_GetStatus() & 0x01) {} // Ждём окончания записи (WIP=0)
}
 
// Чтение одного байта
uint8_t M95256_ReadByte(uint16_t address) {
    uint8_t data;
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x03); // Команда READ
    SPI_Transfer(address >> 8); // Старший байт адреса
    SPI_Transfer(address & 0xFF); // Младший байт адреса
    data = SPI_Transfer(0xFF); // Фиктивный байт для чтения
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
    return data;
}
 
// Запись буфера
void M95256_WriteBuffer(uint16_t address, uint8_t *data, uint16_t length) {
    uint16_t i = 0;
    while (i < length) {
        uint16_t page_remain = 64 - (address % 64); // Остаток до конца страницы
        uint16_t chunk = (length - i < page_remain) ? length - i : page_remain;
 
        M95256_WriteEnable();
        if (!(M95256_GetStatus() & 0x02)) {
            UART_SendString("Error: Write Enable failed in WriteBuffer\r\n");
            return;
        }
        HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
        Delay_us(1);
        SPI_Transfer(0x02); // Команда WRITE
        SPI_Transfer(address >> 8); // Старший байт адреса
        SPI_Transfer(address & 0xFF); // Младший байт адреса
        for (uint16_t j = 0; j < chunk; j++) {
            SPI_Transfer(data[i + j]);
        }
        HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
        Delay_us(1);
        while (M95256_GetStatus() & 0x01) {} // Ждём окончания записи
 
        i += chunk;
        address += chunk;
    }
}
 
// Чтение буфера
void M95256_ReadBuffer(uint16_t address, uint8_t *buffer, uint16_t length) {
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x03); // Команда READ
    SPI_Transfer(address >> 8); // Старший байт адреса
    SPI_Transfer(address & 0xFF); // Младший байт адреса
    for (uint16_t i = 0; i < length; i++) {
        buffer[i] = SPI_Transfer(0xFF); // Читаем данные
    }
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
}
 
// Обработчик прерывания таймера TIM3
void TIM3_IRQHandler(void) {
    if (TIM3->SR & TIM_SR_UIF) {
        TIM3->SR &= ~TIM_SR_UIF;
    }
}
 
// Обработчик прерывания UART
void USART1_IRQHandler(void) {
    if (USART1->ISR & USART_ISR_RXNE) {
        char received = USART1->RDR;
        if (received == '\n') {
            RS485_SetTransmitMode();
            UART_SendString("ECHO: ");
            rx_buffer[rx_index] = '\0';
            UART_SendString(rx_buffer);
            UART_SendString("\r\n");
            RS485_SetReceiveMode();
            rx_index = 0;
        } else {
            if (rx_index < RX_BUFFER_SIZE - 1) {
                rx_buffer[rx_index++] = received;
            } else {
                rx_index = 0;
            }
        }
    }
}
 
// Основная функция
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    UART_Init();
    RS485_Init();
    SPI_Init();
    M95256_Init();
    Timer_Init();
    UART_SendString("madmenat.ru\r\n");
    // Диагностика M95256
    char msg[64];
    uint8_t status = M95256_GetStatus();
    snprintf(msg, sizeof(msg), "M95256 Status: 0x%02X\r\n", status);
    UART_SendString(msg);
    if (status & 0x02) {
        UART_SendString("Write Enabled\r\n");
    } else {
        UART_SendString("Write Disabled\r\n");
    }
 
    // Проверка одного байта перед записью
    uint8_t byte = M95256_ReadByte(0x0000);
    snprintf(msg, sizeof(msg), "Byte at 0x0000 before write: 0x%02X\r\n", byte);
    UART_SendString(msg);
 
    // Тест памяти M95256
    char test_data[] = "TestData";
    uint8_t read_buffer[sizeof(test_data)];
 
    UART_SendString("Writing to M95256...\r\n");
    M95256_WriteBuffer(0x0000, (uint8_t *)test_data, sizeof(test_data));
 
    UART_SendString("Reading from M95256...\r\n");
    M95256_ReadBuffer(0x0000, read_buffer, sizeof(test_data));
 
    UART_SendString("Read data: ");
    read_buffer[sizeof(test_data)-1] = '\0'; // Завершаем строку
    UART_SendString((char *)read_buffer);
    UART_SendString("\r\n");
 
    // Вывод байтов в hex для отладки
    for (uint16_t i = 0; i < sizeof(test_data); i++) {
        snprintf(msg, sizeof(msg), "Byte %d: 0x%02X\r\n", i, read_buffer[i]);
        UART_SendString(msg);
    }
 
    while (1) {
        // Основной цикл пуст
    }
}
 
// Настройка системной частоты
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
 
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}
 
// Инициализация GPIO
static void MX_GPIO_Init(void) {
    // Дополнительная настройка GPIO, если потребуется
}
 
// Обработчик ошибок
void Error_Handler(void) {
    __disable_irq();
    while (1) {}
}
 
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line) {
}
#endif
 

  eeprom stm32 1

 

 

Вывод

Приведённая реализация позволяет легко тестировать работу SPI‑EEPROM M95256‑WMN6TP на плате с STM32F030K6T6 и модулем RS‑485/UART. Вы можете взять этот код за основу для ваших проектов, добавив поддержку команд из консоли, проверку CRC, автоматическое удаление старых данных и т. д. Ну... И еще я сделал итоговое умозаключение из опыта работы с данной микросхемой: EEPROM память на SPI - это кал собачий. Лучше переплатить 20 р. и вместо такой микрухи использовать что-то вроде MB85RC256V.

 

P. S.

А вот еще расширенная версия программы, которая принимает данные через RS485 и возвращает эхо. При получении команды "Enable" она включает светодиод PA8, а при "Disable" выключает его. Тут обработчик приема работает по прерыванию (USART1_IRQHandler), само эхо отправляется по истечению 100 миллисекунд, либо при получению символа "\n". В качестве таймера настроен TIM3, в дополнение к ней для отсчета прошедшего времени реализована функция millis(). Такая же, типа, как у ардуинщиков, только без гомосятской радуги.

#include "main.h"
#include <string.h>
#include <stdio.h>
 
#define SYSCLK_HZ 8000000UL   // Тактовая частота системы: 8 МГц
#define BAUDRATE  115200UL    // Скорость UART: 115200 бод
#define TIMEOUT_MS 100        // Таймаут 100 мс
 
// Пин для управления направлением RS485
#define RS485_CONTROL_PIN GPIO_PIN_1
#define RS485_CONTROL_PORT GPIOF
 
// Пин для Chip Select M95256
#define M95256_CS_PIN GPIO_PIN_4
#define M95256_CS_PORT GPIOA
 
// Буфер для приёма данных UART
#define RX_BUFFER_SIZE 64
char rx_buffer[RX_BUFFER_SIZE] = {0};
uint8_t rx_index = 0;
 
// Переменные для millis()
volatile uint32_t timer_overflows = 0;
volatile uint32_t last_rx_time = 0;
 
// Прототипы функций
void SystemClock_Config(void);
void UART_Init(void);
void Timer_Init(void);
void UART_SendString(char *str);
static void MX_GPIO_Init(void);
void RS485_Init(void);
void RS485_SetTransmitMode(void);
void RS485_SetReceiveMode(void);
void SPI_Init(void);
void M95256_Init(void);
void M95256_WriteEnable(void);
uint8_t M95256_GetStatus(void);
void M95256_WriteByte(uint16_t address, uint8_t data);
uint8_t M95256_ReadByte(uint16_t address);
void M95256_WriteBuffer(uint16_t address, uint8_t *data, uint16_t length);
void M95256_ReadBuffer(uint16_t address, uint8_t *buffer, uint16_t length);
uint32_t millis(void);
 
// Задержка для таймингов (в микросекундах)
void Delay_us(uint32_t us) {
    uint32_t cycles = (SYSCLK_HZ / 1000000UL) * us;
    while (cycles--) {
        __NOP();
    }
}
 
// Функция для получения текущего времени в миллисекундах
uint32_t millis(void) {
    uint32_t overflows;
    uint32_t cnt;
    __disable_irq();
    overflows = timer_overflows;
    cnt = TIM3->CNT;
    __enable_irq();
    return overflows * 1000 + cnt; // 1000 мс на переполнение
}
 
// Инициализация RS485
void RS485_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOF_CLK_ENABLE();
    GPIO_InitStruct.Pin = RS485_CONTROL_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(RS485_CONTROL_PORT, &GPIO_InitStruct);
    RS485_SetReceiveMode();
}
 
// Установка режима передачи для RS485
void RS485_SetTransmitMode(void) {
    HAL_GPIO_WritePin(RS485_CONTROL_PORT, RS485_CONTROL_PIN, GPIO_PIN_SET);
    Delay_us(50);
}
 
// Установка режима приёма для RS485
void RS485_SetReceiveMode(void) {
    HAL_GPIO_WritePin(RS485_CONTROL_PORT, RS485_CONTROL_PIN, GPIO_PIN_RESET);
    Delay_us(50);
}
 
// Инициализация UART
void UART_Init(void) {
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
 
    GPIOA->MODER &= ~(GPIO_MODER_MODER2_Msk | GPIO_MODER_MODER3_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1);
    GPIOA->AFR[0] &= ~((0xF << GPIO_AFRL_AFSEL2_Pos) | (0xF << GPIO_AFRL_AFSEL3_Pos));
    GPIOA->AFR[0] |= ((1 << GPIO_AFRL_AFSEL2_Pos) | (1 << GPIO_AFRL_AFSEL3_Pos));
 
    USART1->CR1 = 0;
    USART1->CR2 = 0;
    USART1->BRR = SYSCLK_HZ / BAUDRATE;
    USART1->CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE);
 
    NVIC_SetPriority(USART1_IRQn, 0);
    NVIC_EnableIRQ(USART1_IRQn);
}
 
// Отправка строки через UART
void UART_SendString(char *str) {
    RS485_SetTransmitMode();
    while (*str) {
        while (!(USART1->ISR & USART_ISR_TXE)) {}
        USART1->TDR = *str++;
    }
    while (!(USART1->ISR & USART_ISR_TC)) {}
    RS485_SetReceiveMode();
}
 
// Инициализация таймера TIM3
void Timer_Init(void) {
    __HAL_RCC_TIM3_CLK_ENABLE();
    TIM3->PSC = (SYSCLK_HZ / 1000) - 1; // 1 мс на тик
    TIM3->ARR = 1000 - 1; // Переполнение каждую 1 с
    TIM3->DIER |= TIM_DIER_UIE;
    NVIC_SetPriority(TIM3_IRQn, 1);
    NVIC_EnableIRQ(TIM3_IRQn);
    TIM3->CR1 |= TIM_CR1_CEN;
}
 
// Инициализация SPI
void SPI_Init(void) {
    __HAL_RCC_SPI1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
 
    GPIOA->MODER &= ~(GPIO_MODER_MODER5_Msk | GPIO_MODER_MODER6_Msk | GPIO_MODER_MODER7_Msk);
    GPIOA->MODER |= (GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1);
    GPIOA->AFR[0] &= ~((0xF << GPIO_AFRL_AFSEL5_Pos) | (0xF << GPIO_AFRL_AFSEL6_Pos) | (0xF << GPIO_AFRL_AFSEL7_Pos));
    GPIOA->AFR[0] |= ((0 << GPIO_AFRL_AFSEL5_Pos) | (0 << GPIO_AFRL_AFSEL6_Pos) | (0 << GPIO_AFRL_AFSEL7_Pos));
 
    SPI1->CR1 = 0;
    SPI1->CR1 |= (SPI_CR1_MSTR | SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPOL | SPI_CR1_CPHA);
    SPI1->CR2 = 0;
    SPI1->CR2 |= (7 << SPI_CR2_DS_Pos);
    SPI1->CR1 |= SPI_CR1_SPE;
}
 
// Инициализация M95256
void M95256_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = M95256_CS_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(M95256_CS_PORT, &GPIO_InitStruct);
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(100);
}
 
// SPI и M95256 функции без изменений
uint8_t SPI_Transfer(uint8_t data) {
    while (!(SPI1->SR & SPI_SR_TXE)) {}
    SPI1->DR = data;
    while (!(SPI1->SR & SPI_SR_RXNE)) {}
    return SPI1->DR;
}
 
void M95256_WriteEnable(void) {
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x06);
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
}
 
uint8_t M95256_GetStatus(void) {
    uint8_t status;
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x05);
    status = SPI_Transfer(0xFF);
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
    return status;
}
 
void M95256_WriteByte(uint16_t address, uint8_t data) {
    M95256_WriteEnable();
    if (!(M95256_GetStatus() & 0x02)) {
        UART_SendString("Error: Write Enable failed\r\n");
        return;
    }
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x02);
    SPI_Transfer(address >> 8);
    SPI_Transfer(address & 0xFF);
    SPI_Transfer(data);
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
    while (M95256_GetStatus() & 0x01) {}
}
 
uint8_t M95256_ReadByte(uint16_t address) {
    uint8_t data;
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x03);
    SPI_Transfer(address >> 8);
    SPI_Transfer(address & 0xFF);
    data = SPI_Transfer(0xFF);
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
    return data;
}
 
void M95256_WriteBuffer(uint16_t address, uint8_t *data, uint16_t length) {
    uint16_t i = 0;
    while (i < length) {
        uint16_t page_remain = 64 - (address % 64);
        uint16_t chunk = (length - i < page_remain) ? length - i : page_remain;
 
        M95256_WriteEnable();
        if (!(M95256_GetStatus() & 0x02)) {
            UART_SendString("Error: Write Enable failed in WriteBuffer\r\n");
            return;
        }
        HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
        Delay_us(1);
        SPI_Transfer(0x02);
        SPI_Transfer(address >> 8);
        SPI_Transfer(address & 0xFF);
        for (uint16_t j = 0; j < chunk; j++) {
            SPI_Transfer(data[i + j]);
        }
        HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
        Delay_us(1);
        while (M95256_GetStatus() & 0x01) {}
 
        i += chunk;
        address += chunk;
    }
}
 
void M95256_ReadBuffer(uint16_t address, uint8_t *buffer, uint16_t length) {
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_RESET);
    Delay_us(1);
    SPI_Transfer(0x03);
    SPI_Transfer(address >> 8);
    SPI_Transfer(address & 0xFF);
    for (uint16_t i = 0; i < length; i++) {
        buffer[i] = SPI_Transfer(0xFF);
    }
    HAL_GPIO_WritePin(M95256_CS_PORT, M95256_CS_PIN, GPIO_PIN_SET);
    Delay_us(1);
}
 
// Обработчик прерывания таймера TIM3
void TIM3_IRQHandler(void) {
    if (TIM3->SR & TIM_SR_UIF) {
        TIM3->SR &= ~TIM_SR_UIF;
        timer_overflows++; // Увеличиваем счётчик переполнений
    }
}
 
// Обработчик прерывания UART
void USART1_IRQHandler(void) {
    if (USART1->ISR & USART_ISR_RXNE) {
        char received = USART1->RDR;
        last_rx_time = millis(); // Запоминаем время последнего байта
        if (received == '\n') {
            rx_buffer[rx_index] = '\0';
            if (rx_index > 0 && rx_buffer[rx_index-1] == '\r') {
                rx_buffer[rx_index-1] = '\0';
            }
            RS485_SetTransmitMode();
            Delay_us(50);
            if (strcmp(rx_buffer, "Enable") == 0) {
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
                UART_SendString("LED Enabled\r\n");
            } else if (strcmp(rx_buffer, "Disable") == 0) {
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
                UART_SendString("LED Disabled\r\n");
            } else {
                UART_SendString("ECHO: ");
                UART_SendString(rx_buffer);
                UART_SendString("\r\n");
            }
            RS485_SetReceiveMode();
            rx_index = 0;
        } else {
            if (rx_index < RX_BUFFER_SIZE - 1) {
                rx_buffer[rx_index++] = received;
            } else {
                rx_index = 0;
            }
        }
    }
    if (USART1->ISR & (USART_ISR_ORE | USART_ISR_PE | USART_ISR_FE)) {
        USART1->ICR |= USART_ICR_ORECF | USART_ICR_PECF | USART_ICR_FECF;
        RS485_SetTransmitMode();
        Delay_us(50);
        UART_SendString("UART Error\r\n");
        RS485_SetReceiveMode();
    }
}
 
// Основная функция
int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    UART_Init();
    RS485_Init();
    SPI_Init();
    M95256_Init();
    Timer_Init();
    UART_SendString("madmenat.ru\r\n");
 
    // Диагностика M95256
    char msg[64];
    uint8_t status = M95256_GetStatus();
    snprintf(msg, sizeof(msg), "M95256 Status: 0x%02X\r\n", status);
    UART_SendString(msg);
    if (status & 0x02) {
        UART_SendString("Write Enabled\r\n");
    } else {
        UART_SendString("Write Disabled\r\n");
    }
 
    uint8_t byte = M95256_ReadByte(0x0000);
    snprintf(msg, sizeof(msg), "Byte at 0x0000 before write: 0x%02X\r\n", byte);
    UART_SendString(msg);
 
    char test_data[] = "TestData";
    uint8_t read_buffer[sizeof(test_data)];
 
    UART_SendString("Writing to M95256...\r\n");
    M95256_WriteBuffer(0x0000, (uint8_t *)test_data, sizeof(test_data));
 
    UART_SendString("Reading from M95256...\r\n");
    M95256_ReadBuffer(0x0000, read_buffer, sizeof(test_data));
 
    UART_SendString("Read data: ");
    read_buffer[sizeof(test_data)-1] = '\0';
    UART_SendString((char *)read_buffer);
    UART_SendString("\r\n");
 
    for (uint16_t i = 0; i < sizeof(test_data); i++) {
        snprintf(msg, sizeof(msg), "Byte %d: 0x%02X\r\n", i, read_buffer[i]);
        UART_SendString(msg);
    }
 
    while (1) {
        if (rx_index > 0 && (millis() - last_rx_time >= TIMEOUT_MS)) {
            rx_buffer[rx_index] = '\0';
            if (rx_index > 0 && rx_buffer[rx_index-1] == '\r') {
                rx_buffer[rx_index-1] = '\0';
            }
            RS485_SetTransmitMode();
            Delay_us(50);
            if (strcmp(rx_buffer, "Enable") == 0) {
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
                UART_SendString("LED Enabled\r\n");
            } else if (strcmp(rx_buffer, "Disable") == 0) {
                HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
                UART_SendString("LED Disabled\r\n");
            } else {
                UART_SendString("ECHO: ");
                UART_SendString(rx_buffer);
                UART_SendString("\r\n");
            }
            RS485_SetReceiveMode();
            rx_index = 0;
        }
    }
}
 
// Настройка системной частоты
void SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
 
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);
 
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}
 
// Инициализация GPIO
static void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitStruct.Pin = GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
}
 
// Обработчик ошибок
void Error_Handler(void) {
    __disable_irq();
    while (1) {}
}
 
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line) {
}
#endif