/** * @file eeprom_m95.c * @author xxx * @date 2023-08-30 08:58:43 * @brief 用于实现M95 EEPROM相关的读写操作 * @copyright Copyright (c) 2023 by xxx, All Rights Reserved. */ #include "eeprom_m95.h" #include "spis.h" #include "delay.h" #include "entity.h" #include "board.h" #include "diagnosis.h" #define M95_SPI SPI1 #define M95_DMA DMA1 #define M95_DMA_RX_CHANNEL LL_DMA_CHANNEL_2 #define M95_DMA_TX_CHANNEL LL_DMA_CHANNEL_3 #define EEPROM_M95_1_CS_PORT EE1_CS_GPIO_Port #define EEPROM_M95_1_CS_PIN EE1_CS_Pin #define EEPROM_M95_2_CS_PORT EE2_CS_GPIO_Port #define EEPROM_M95_2_CS_PIN EE2_CS_Pin // 下面宏定义为2个EEPROM_M95的引脚定义 #define EEPROM_M95_MOSI_PORT SPI_MOSI_GPIO_Port #define EEPROM_M95_MOSI_PIN SPI_MOSI_Pin #define EEPROM_M95_MISO_PORT SPI_MISO_GPIO_Port #define EEPROM_M95_MISO_PIN SPI_MISO_Pin #define EEPROM_M95_SCK_PORT SPI_CLK_GPIO_Port #define EEPROM_M95_SCK_PIN SPI_CLK_Pin __IO m95_number_e current_m95_number; m95_number_t eeprom_m95s[M95_MAX]; static void write_enable(spi_t *eeprom_m95_spi); // 写使能 static void write_disable(spi_t *eeprom_m95_spi); // 写保护 static uint8_t read_status(spi_t *eeprom_m95_spi); // 读状态 static void eeprom_m95_ready(m95_number_e num); // 等待就绪 // DMA TX回调函数 static void dma_tx_cb(spi_t *handle) { DMA_ClEAR_FLAG(handle->dma, 3, handle->tx_dma_ok); } // DMA RX回调函数 static void dma_rx_cb(spi_t *handle) { DMA_ClEAR_FLAG(handle->dma, 2, handle->rx_dma_ok); } /** * @brief 初始化EEPROM_M95eeprom_m95s * @param {m95_number_e} num * @return {*} * @note 初始化函数对板卡上不同的芯片定义了块大小 */ void eeprom_m95_init(m95_number_e num) { DBG_ASSERT(num < M95_MAX __DBG_LINE); current_m95_number = num; spi_gpio_group_t gpios; spi_t *eeprom_m95_spi; // 128 byte if (num == M95_1) { // 创建CS引脚 gpios.cs = gpio_create(EEPROM_M95_1_CS_PORT, EEPROM_M95_1_CS_PIN); // 设置页面大小 eeprom_m95s[num].page_size = M95_PAGE_SIZE_128; // 设置总大小 eeprom_m95s[num].total_size = _M95512_; eeprom_m95s[num].address_bytes = 2; } // 256 byte else if (num == M95_2) { // 创建CS引脚 gpios.cs = gpio_create(EEPROM_M95_2_CS_PORT, EEPROM_M95_2_CS_PIN); // 设置页面大小 eeprom_m95s[num].page_size = M95_PAGE_SIZE_256; // 设置总大小 eeprom_m95s[num].total_size = _M95M02_; eeprom_m95s[num].address_bytes = 3; } else { DBG_ASSERT(FALSE __DBG_LINE); } eeprom_m95s[num].rxbuf = (uint8_t *)osel_mem_alloc(eeprom_m95s[num].page_size + (eeprom_m95s[num].address_bytes + 1) * 2); eeprom_m95s[num].txbuf = (uint8_t *)osel_mem_alloc(eeprom_m95s[num].page_size + eeprom_m95s[num].address_bytes + 1); gpios.mosi = gpio_create(SPI_MOSI_GPIO_Port, SPI_MOSI_Pin); gpios.sck = gpio_create(SPI_CLK_GPIO_Port, SPI_CLK_Pin); gpios.miso = gpio_create(SPI_MISO_GPIO_Port, SPI_MISO_Pin); gpios.rst = gpio_create(NULL, 0); gpios.rdy = gpio_create(NULL, 0); // 创建SPI对象 eeprom_m95_spi = spi_create(SPI_TYPE_NORMAL, gpios, 0); DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); // 使能SPI eeprom_m95_spi->interface.hardware_enable(eeprom_m95_spi, M95_SPI); eeprom_m95_spi->interface.dma_enable(eeprom_m95_spi, M95_DMA, M95_DMA_RX_CHANNEL, dma_rx_cb, M95_DMA_TX_CHANNEL, dma_tx_cb); eeprom_m95s[num].num = num; eeprom_m95s[num].spi = eeprom_m95_spi; // 这里需要设置,否则读出的数据不对 eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); delay_tick(10); eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); delay_tick(10); // eeprom_m95_test(num); } /** * @brief 反初始化EEPROM_M95 * @param {m95_number_e} num * @return {*} * @note */ void eeprom_m95_dinit(m95_number_e num) { LL_SPI_Disable(M95_SPI); GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.mosi->port, eeprom_m95s[num].spi->gpios.mosi->pin); GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.miso->port, eeprom_m95s[num].spi->gpios.miso->pin); GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.sck->port, eeprom_m95s[num].spi->gpios.sck->pin); GPIO_SET_ANALOG(eeprom_m95s[num].spi->gpios.cs->port, eeprom_m95s[num].spi->gpios.cs->pin); } /** * @brief M95 EEPROM使能 * @return {*} * @note */ void eeprom_m95_enable(void) { LL_SPI_Enable(M95_SPI); } /** * @brief M95 EEPROM失能 * @return {*} * @note */ void eeprom_m95_disable(void) { LL_SPI_Disable(M95_SPI); } /** * @brief 读取M95 EEPROM内存数据 * @param num EEPROM模块编号(0或1) * @param read_addr 要读取的地址 * @param data 存储读取数据的缓冲区 * @param length 要读取的数据长度 * @return {*} */ void eeprom_m95_read(m95_number_e num, uint32_t read_addr, uint8_t *data, uint16_t length) { current_m95_number = num; spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; // 获取EEPROM模块的SPI配置 eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信 eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_CMD_READ); // 发送读取命令 if (eeprom_m95s[num].address_bytes == 2) { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, read_addr >> 8); // 发送高位地址 eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, read_addr); // 发送低位地址 } else { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, read_addr >> 16); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, read_addr >> 8); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, read_addr); } for (uint16_t i = 0; i < length; i++) // 循环读取数据 { data[i] = eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_DUMMY_BYTE); // 发送空字节,读取实际数据 } eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); // 设置CS引脚为高电平,完成SPI通信 } /** * @brief 向M95 EEPROM内存写入数据 * @param num EEPROM模块编号(0或1) * @param write_addr 要写入的地址 * @param data 包含要写入数据的缓冲区 * @param length 要写入的数据长度 * @return {*} */ void eeprom_m95_write(m95_number_e num, uint32_t write_addr, uint8_t *data, uint16_t length) { current_m95_number = num; spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; uint32_t page_size = eeprom_m95s[num].page_size; DBG_ASSERT(eeprom_m95_spi != NULL __DBG_LINE); write_enable(eeprom_m95_spi); // 写入使能命令 eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信 eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_CMD_WRITE); // 发送写入命令 if (eeprom_m95s[num].address_bytes == 2) { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr >> 8); // 发送高位地址 eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr); // 发送低位地址 } else { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr >> 16); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr >> 8); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr); } while (length--) { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, *data); // 发送一个字节数据 data++; write_addr++; if (((write_addr % page_size) == 0) && (length > 0)) { // 一页写完 eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); // 设置CS引脚为高电平,完成SPI通信 eeprom_m95_ready(num); write_enable(eeprom_m95_spi); // 写入使能命令 eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); // 设置CS引脚为低电平,准备开始SPI通信 eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_CMD_WRITE); // 发送写入命令 if (eeprom_m95s[num].address_bytes == 2) { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr >> 8); // 发送高位地址 eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr); // 发送低位地址 } else { eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr >> 16); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr >> 8); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, write_addr); } } } eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); // 设置CS引脚为高电平,完成SPI通信 eeprom_m95_ready(num); write_disable(eeprom_m95_spi); } /** * @brief 用于M95 EEPROM测试 * @param {m95_number_e} num * @return {*} * @note */ void eeprom_m95_test(m95_number_e num) { #define TEST_LEN 10 DBG_ASSERT(eeprom_m95s[num].spi != NULL __DBG_LINE); uint16_t test_address = 0; uint8_t buf[TEST_LEN]; buf[0] = 0xA5; buf[1] = 0xC8; buf[2] = 0x00; buf[3] = 0x03; buf[4] = num; buf[5] = 0xaa; buf[6] = 0xbb; buf[TEST_LEN - 1] = 0xce; eeprom_m95_write(num, test_address, buf, TEST_LEN); osel_memset(buf, 0, ARRAY_LEN(buf)); eeprom_m95_read(num, test_address, buf, TEST_LEN); __NOP(); } /** * @brief M95 EEPROM写使能 * @param {spi_t} *eeprom_m95_spi * @return {*} * @note */ static void write_enable(spi_t *eeprom_m95_spi) { eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_CMD_WREN); eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); } /** * @brief M95 EEPROM写保护 * @param {spi_t} *eeprom_m95_spi * @return {*} * @note */ static void write_disable(spi_t *eeprom_m95_spi) { eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_CMD_WRDI); eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); } /** * @brief M95 EEPROM读状态 * @param {spi_t} *eeprom_m95_spi * @return {*} * @note */ static uint8_t read_status(spi_t *eeprom_m95_spi) { uint8_t data; eeprom_m95_spi->gpios.cs->reset(*eeprom_m95_spi->gpios.cs); eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_CMD_RDSR); data = eeprom_m95_spi->interface.u.normal.spi_send(eeprom_m95_spi, M95_DUMMY_BYTE); eeprom_m95_spi->gpios.cs->set(*eeprom_m95_spi->gpios.cs); return data; } /** * @brief M95 EEPROM等待就绪 * @param {m95_number_e} num * @return {*} * @note */ static void eeprom_m95_ready(m95_number_e num) { spi_t *eeprom_m95_spi = eeprom_m95s[num].spi; uint16_t count = 0; while (read_status(eeprom_m95_spi) & 0x01) { if (count++ > 2000) { __NOP(); break; } } }