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 циклов)
- Необходимость задержек или чтения статуса для завершения записи
- Невысокая скорость при последовательной записи, если не использовать страничную арх.
Подключение
STM32F030K6T6 | M95256‑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 подтянуты |
![]() |
![]() |
Структура примера кода
- Инициализация системного тактирования (8 МГц HSI)
- Настройка GPIO для CS, RS‑485, UART
- Инициализация UART/RS‑485 для вывода результатов теста
- Инициализация SPI1 (Master, CPOL=1, CPHA=1, 8 бит, 500 кГц)
- Драйвер M95256:
- ⮕
WriteEnable()
— разрешение записи - ⮕
GetStatus()
— чтение регистра статуса - ⮕
WriteByte/ReadByte()
— запись/чтение одного байта - ⮕
WriteBuffer/ReadBuffer()
— работа с блоками и страничная привязка
- ⮕
- Тестирование памяти:
- Считываем статус (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
Вывод
Приведённая реализация позволяет легко тестировать работу 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