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, 8VCC, HOLD, WP подтянуты
m32f030k6t6 pinout 1M95256 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