ioc changed but not tested

This commit is contained in:
TuxMonkey
2026-07-20 15:39:14 +08:00
parent d13729b162
commit cda936d7e0
5 changed files with 1063 additions and 0 deletions

View File

@@ -0,0 +1,552 @@
#include "tft.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#define TFT_SPI_TIMEOUT_MS 100U
#define TFT_FONT_W 5U
#define TFT_FONT_H 7U
#define TFT_MAX_SCALE 4U
#if (TFT_USE_HORIZONTAL == 0U) || (TFT_USE_HORIZONTAL == 1U)
#define TFT_X_OFFSET 0U
#define TFT_Y_OFFSET 20U
#else
#define TFT_X_OFFSET 20U
#define TFT_Y_OFFSET 0U
#endif
typedef enum
{
TFT_GLYPH_SPACE = 0,
TFT_GLYPH_DASH,
TFT_GLYPH_DOT,
TFT_GLYPH_COLON,
TFT_GLYPH_0,
TFT_GLYPH_A = TFT_GLYPH_0 + 10
} TFT_GlyphIndex_e;
static bool tft_initialized = false;
static const uint8_t tft_font5x7[][TFT_FONT_H] = {
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* space */
{0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00}, /* - */
{0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C}, /* . */
{0x00, 0x0C, 0x0C, 0x00, 0x0C, 0x0C, 0x00}, /* : */
{0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E}, /* 0 */
{0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E}, /* 1 */
{0x0E, 0x11, 0x10, 0x08, 0x04, 0x02, 0x1F}, /* 2 */
{0x1F, 0x08, 0x04, 0x08, 0x10, 0x11, 0x0E}, /* 3 */
{0x08, 0x0C, 0x0A, 0x09, 0x1F, 0x08, 0x08}, /* 4 */
{0x1F, 0x01, 0x0F, 0x10, 0x10, 0x11, 0x0E}, /* 5 */
{0x0C, 0x02, 0x01, 0x0F, 0x11, 0x11, 0x0E}, /* 6 */
{0x1F, 0x10, 0x08, 0x04, 0x02, 0x02, 0x02}, /* 7 */
{0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E}, /* 8 */
{0x0E, 0x11, 0x11, 0x1E, 0x10, 0x08, 0x06}, /* 9 */
{0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, /* A */
{0x0F, 0x11, 0x11, 0x0F, 0x11, 0x11, 0x0F}, /* B */
{0x0E, 0x11, 0x01, 0x01, 0x01, 0x11, 0x0E}, /* C */
{0x0F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0F}, /* D */
{0x1F, 0x01, 0x01, 0x0F, 0x01, 0x01, 0x1F}, /* E */
{0x1F, 0x01, 0x01, 0x0F, 0x01, 0x01, 0x01}, /* F */
{0x0E, 0x11, 0x01, 0x1D, 0x11, 0x11, 0x0E}, /* G */
{0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11}, /* H */
{0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E}, /* I */
{0x1C, 0x08, 0x08, 0x08, 0x08, 0x09, 0x06}, /* J */
{0x11, 0x09, 0x05, 0x03, 0x05, 0x09, 0x11}, /* K */
{0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x1F}, /* L */
{0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11}, /* M */
{0x11, 0x13, 0x15, 0x19, 0x11, 0x11, 0x11}, /* N */
{0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, /* O */
{0x0F, 0x11, 0x11, 0x0F, 0x01, 0x01, 0x01}, /* P */
{0x0E, 0x11, 0x11, 0x11, 0x15, 0x09, 0x16}, /* Q */
{0x0F, 0x11, 0x11, 0x0F, 0x05, 0x09, 0x11}, /* R */
{0x1E, 0x01, 0x01, 0x0E, 0x10, 0x10, 0x0F}, /* S */
{0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}, /* T */
{0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E}, /* U */
{0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04}, /* V */
{0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A}, /* W */
{0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11}, /* X */
{0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04}, /* Y */
{0x1F, 0x10, 0x08, 0x04, 0x02, 0x01, 0x1F}, /* Z */
};
static void tft_cs_low(void)
{
HAL_GPIO_WritePin(TFT_CS_GPIO_Port, TFT_CS_Pin, GPIO_PIN_RESET);
}
static void tft_cs_high(void)
{
HAL_GPIO_WritePin(TFT_CS_GPIO_Port, TFT_CS_Pin, GPIO_PIN_SET);
}
static void tft_dc_command(void)
{
HAL_GPIO_WritePin(TFT_DC_GPIO_Port, TFT_DC_Pin, GPIO_PIN_RESET);
}
static void tft_dc_data(void)
{
HAL_GPIO_WritePin(TFT_DC_GPIO_Port, TFT_DC_Pin, GPIO_PIN_SET);
}
static void tft_reset_low(void)
{
HAL_GPIO_WritePin(TFT_RES_GPIO_Port, TFT_RES_Pin, GPIO_PIN_RESET);
}
static void tft_reset_high(void)
{
HAL_GPIO_WritePin(TFT_RES_GPIO_Port, TFT_RES_Pin, GPIO_PIN_SET);
}
static void tft_backlight_on(void)
{
HAL_GPIO_WritePin(TFT_BLK_GPIO_Port, TFT_BLK_Pin, GPIO_PIN_SET);
}
static void tft_write_bytes(const uint8_t *data, uint32_t len)
{
while (len > 0U)
{
uint16_t chunk = len > UINT16_MAX ? UINT16_MAX : (uint16_t) len;
(void) HAL_SPI_Transmit(&TFT_SPI_UNIT, (uint8_t *) data, chunk, TFT_SPI_TIMEOUT_MS);
data += chunk;
len -= chunk;
}
}
static void lcd_write_reg(uint8_t reg)
{
tft_cs_low();
tft_dc_command();
tft_write_bytes(&reg, 1U);
tft_cs_high();
}
static void lcd_write_data8(uint8_t data)
{
tft_cs_low();
tft_dc_data();
tft_write_bytes(&data, 1U);
tft_cs_high();
}
static void lcd_write_data16(uint16_t data)
{
uint8_t buf[2] = {(uint8_t) (data >> 8), (uint8_t) data};
tft_cs_low();
tft_dc_data();
tft_write_bytes(buf, sizeof(buf));
tft_cs_high();
}
static void tft_write_color_block(uint16_t color, uint32_t count)
{
uint8_t buf[64];
uint8_t high = (uint8_t) (color >> 8);
uint8_t low = (uint8_t) color;
for (uint32_t i = 0U; i < sizeof(buf); i += 2U)
{
buf[i] = high;
buf[i + 1U] = low;
}
tft_cs_low();
tft_dc_data();
while (count > 0U)
{
uint32_t pixels = count > (sizeof(buf) / 2U) ? (sizeof(buf) / 2U) : count;
tft_write_bytes(buf, pixels * 2U);
count -= pixels;
}
tft_cs_high();
}
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
uint16_t xs = x1 + TFT_X_OFFSET;
uint16_t xe = x2 + TFT_X_OFFSET;
uint16_t ys = y1 + TFT_Y_OFFSET;
uint16_t ye = y2 + TFT_Y_OFFSET;
lcd_write_reg(0x2A);
lcd_write_data16(xs);
lcd_write_data16(xe);
lcd_write_reg(0x2B);
lcd_write_data16(ys);
lcd_write_data16(ye);
lcd_write_reg(0x2C);
}
void TFT_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color)
{
if (x >= TFT_LCD_W || y >= TFT_LCD_H || width == 0U || height == 0U)
{
return;
}
if ((uint32_t) x + width > TFT_LCD_W)
{
width = (uint16_t) (TFT_LCD_W - x);
}
if ((uint32_t) y + height > TFT_LCD_H)
{
height = (uint16_t) (TFT_LCD_H - y);
}
LCD_Address_Set(x, y, (uint16_t) (x + width - 1U), (uint16_t) (y + height - 1U));
tft_write_color_block(color, (uint32_t) width * height);
}
void LCD_Fill(uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend, uint16_t color)
{
if (xend <= xsta || yend <= ysta)
{
return;
}
TFT_FillRect(xsta, ysta, (uint16_t) (xend - xsta), (uint16_t) (yend - ysta), color);
}
void TFT_DrawPoint(uint16_t x, uint16_t y, uint16_t color)
{
TFT_FillRect(x, y, 1U, 1U, color);
}
static const uint8_t *tft_get_glyph(char ch)
{
if (ch >= 'a' && ch <= 'z')
{
ch = (char) (ch - ('a' - 'A'));
}
if (ch >= '0' && ch <= '9')
{
return tft_font5x7[TFT_GLYPH_0 + (uint8_t) (ch - '0')];
}
if (ch >= 'A' && ch <= 'Z')
{
return tft_font5x7[TFT_GLYPH_A + (uint8_t) (ch - 'A')];
}
switch (ch)
{
case '-':
return tft_font5x7[TFT_GLYPH_DASH];
case '.':
return tft_font5x7[TFT_GLYPH_DOT];
case ':':
return tft_font5x7[TFT_GLYPH_COLON];
default:
return tft_font5x7[TFT_GLYPH_SPACE];
}
}
static void tft_draw_char(uint16_t x, uint16_t y, char ch, uint16_t fc, uint16_t bc, uint8_t scale)
{
uint8_t line[(TFT_FONT_W * TFT_MAX_SCALE) * 2U];
const uint8_t *glyph;
uint16_t char_w;
uint16_t char_h;
if (scale == 0U)
{
scale = 1U;
}
if (scale > TFT_MAX_SCALE)
{
scale = TFT_MAX_SCALE;
}
char_w = TFT_FONT_W * scale;
char_h = TFT_FONT_H * scale;
if (x >= TFT_LCD_W || y >= TFT_LCD_H)
{
return;
}
glyph = tft_get_glyph(ch);
LCD_Address_Set(x, y, (uint16_t) (x + char_w - 1U), (uint16_t) (y + char_h - 1U));
tft_cs_low();
tft_dc_data();
for (uint8_t row = 0U; row < TFT_FONT_H; row++)
{
for (uint8_t repeat_y = 0U; repeat_y < scale; repeat_y++)
{
uint32_t idx = 0U;
for (uint8_t col = 0U; col < TFT_FONT_W; col++)
{
uint16_t color = (glyph[row] & (1U << (TFT_FONT_W - 1U - col))) ? fc : bc;
for (uint8_t repeat_x = 0U; repeat_x < scale; repeat_x++)
{
line[idx++] = (uint8_t) (color >> 8);
line[idx++] = (uint8_t) color;
}
}
tft_write_bytes(line, idx);
}
}
tft_cs_high();
}
void TFT_DrawString(uint16_t x, uint16_t y, const char *text, uint16_t fc, uint16_t bc, uint8_t scale)
{
uint16_t cursor = x;
uint16_t advance;
if (text == NULL)
{
return;
}
if (scale == 0U)
{
scale = 1U;
}
if (scale > TFT_MAX_SCALE)
{
scale = TFT_MAX_SCALE;
}
advance = (TFT_FONT_W + 1U) * scale;
while (*text != '\0')
{
if (cursor + (TFT_FONT_W * scale) >= TFT_LCD_W)
{
break;
}
tft_draw_char(cursor, y, *text, fc, bc, scale);
cursor = (uint16_t) (cursor + advance);
text++;
}
}
void TFT_Clear(uint16_t color)
{
LCD_Fill(0U, 0U, TFT_LCD_W, TFT_LCD_H, color);
}
void LCD_Init(void)
{
if (tft_initialized)
{
return;
}
tft_cs_high();
tft_reset_low();
HAL_Delay(100);
tft_reset_high();
HAL_Delay(100);
tft_backlight_on();
HAL_Delay(100);
lcd_write_reg(0x11);
HAL_Delay(120);
lcd_write_reg(0x36);
#if TFT_USE_HORIZONTAL == 0U
lcd_write_data8(0x00);
#elif TFT_USE_HORIZONTAL == 1U
lcd_write_data8(0xC0);
#elif TFT_USE_HORIZONTAL == 2U
lcd_write_data8(0x70);
#else
lcd_write_data8(0xA0);
#endif
lcd_write_reg(0x3A);
lcd_write_data8(0x05);
lcd_write_reg(0xB2);
lcd_write_data8(0x0C);
lcd_write_data8(0x0C);
lcd_write_data8(0x00);
lcd_write_data8(0x33);
lcd_write_data8(0x33);
lcd_write_reg(0xB7);
lcd_write_data8(0x35);
lcd_write_reg(0xBB);
lcd_write_data8(0x32);
lcd_write_reg(0xC2);
lcd_write_data8(0x01);
lcd_write_reg(0xC3);
lcd_write_data8(0x15);
lcd_write_reg(0xC4);
lcd_write_data8(0x20);
lcd_write_reg(0xC6);
lcd_write_data8(0x0F);
lcd_write_reg(0xD0);
lcd_write_data8(0xA4);
lcd_write_data8(0xA1);
lcd_write_reg(0xE0);
lcd_write_data8(0xD0);
lcd_write_data8(0x08);
lcd_write_data8(0x0E);
lcd_write_data8(0x09);
lcd_write_data8(0x09);
lcd_write_data8(0x05);
lcd_write_data8(0x31);
lcd_write_data8(0x33);
lcd_write_data8(0x48);
lcd_write_data8(0x17);
lcd_write_data8(0x14);
lcd_write_data8(0x15);
lcd_write_data8(0x31);
lcd_write_data8(0x34);
lcd_write_reg(0xE1);
lcd_write_data8(0xD0);
lcd_write_data8(0x08);
lcd_write_data8(0x0E);
lcd_write_data8(0x09);
lcd_write_data8(0x09);
lcd_write_data8(0x15);
lcd_write_data8(0x31);
lcd_write_data8(0x33);
lcd_write_data8(0x48);
lcd_write_data8(0x17);
lcd_write_data8(0x14);
lcd_write_data8(0x15);
lcd_write_data8(0x31);
lcd_write_data8(0x34);
lcd_write_reg(0x21);
lcd_write_reg(0x29);
tft_initialized = true;
TFT_Clear(TFT_COLOR_BLACK);
}
void TFT_Init(void)
{
LCD_Init();
}
const char *TFT_RobotModeText(RobotMode_t mode)
{
switch (mode)
{
case NORMAL_MODE:
return "NORMAL";
case SYS_ERROR_OCCURRED:
return "ERROR";
case REMOTE_NOT_CONNECTED:
return "REMOTE OFF";
case REMOTE_CONNECTED:
return "REMOTE ON";
case AUTO_SHOOTING_MODE:
return "AUTO SHOOT";
case IMU_CALIBERATION_MODE:
return "IMU CAL";
default:
return "UNKNOWN";
}
}
static uint16_t tft_mode_color(RobotMode_t mode)
{
switch (mode)
{
case NORMAL_MODE:
return TFT_COLOR_GREEN;
case SYS_ERROR_OCCURRED:
case REMOTE_NOT_CONNECTED:
return TFT_COLOR_RED;
case REMOTE_CONNECTED:
return TFT_COLOR_YELLOW;
case AUTO_SHOOTING_MODE:
return TFT_COLOR_CYAN;
case IMU_CALIBERATION_MODE:
return TFT_COLOR_MAGENTA;
default:
return TFT_COLOR_LIGHT_GRAY;
}
}
static int16_t tft_temperature_to_tenths(float temperature_c)
{
return (int16_t) ((temperature_c * 10.0f) + (temperature_c >= 0.0f ? 0.5f : -0.5f));
}
static void tft_format_temperature(char *buf, size_t len, int16_t temperature_tenths)
{
uint16_t abs_temp;
if (buf == NULL || len == 0U)
{
return;
}
if (temperature_tenths < 0)
{
abs_temp = (uint16_t) (-temperature_tenths);
(void) snprintf(buf, len, "-%u.%u C", abs_temp / 10U, abs_temp % 10U);
}
else
{
abs_temp = (uint16_t) temperature_tenths;
(void) snprintf(buf, len, "%u.%u C", abs_temp / 10U, abs_temp % 10U);
}
}
static void tft_draw_status_layout(void)
{
TFT_Clear(TFT_COLOR_BLACK);
TFT_DrawString(16U, 16U, "DM LCD", TFT_COLOR_CYAN, TFT_COLOR_BLACK, 3U);
TFT_FillRect(16U, 54U, 248U, 2U, TFT_COLOR_DARK_GRAY);
TFT_DrawString(16U, 76U, "TEMP:", TFT_COLOR_YELLOW, TFT_COLOR_BLACK, 2U);
TFT_DrawString(16U, 132U, "MODE:", TFT_COLOR_YELLOW, TFT_COLOR_BLACK, 2U);
}
void TFT_ShowStatus(float temperature_c, RobotMode_t mode)
{
static bool layout_ready = false;
static int16_t last_temperature_tenths = INT16_MIN;
static RobotMode_t last_mode = (RobotMode_t) 0xFF;
int16_t temperature_tenths = tft_temperature_to_tenths(temperature_c);
char temperature_text[16];
const char *mode_text = TFT_RobotModeText(mode);
if (!tft_initialized)
{
TFT_Init();
}
if (!layout_ready)
{
tft_draw_status_layout();
layout_ready = true;
}
if (temperature_tenths != last_temperature_tenths)
{
uint16_t temperature_color = temperature_tenths >= 550 ? TFT_COLOR_RED : TFT_COLOR_WHITE;
tft_format_temperature(temperature_text, sizeof(temperature_text), temperature_tenths);
TFT_FillRect(96U, 68U, 168U, 32U, TFT_COLOR_BLACK);
TFT_DrawString(100U, 70U, temperature_text, temperature_color, TFT_COLOR_BLACK, 3U);
last_temperature_tenths = temperature_tenths;
}
if (mode != last_mode)
{
TFT_FillRect(96U, 124U, 184U, 32U, TFT_COLOR_BLACK);
TFT_DrawString(100U, 126U, mode_text, tft_mode_color(mode), TFT_COLOR_BLACK, 3U);
last_mode = mode;
}
}

View File

@@ -0,0 +1,86 @@
#ifndef TFT_H
#define TFT_H
#include "main.h"
#include "robot_def.h"
#include "spi.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define TFT_USE_HORIZONTAL 2U
#if (TFT_USE_HORIZONTAL == 0U) || (TFT_USE_HORIZONTAL == 1U)
#define TFT_LCD_W 240U
#define TFT_LCD_H 280U
#else
#define TFT_LCD_W 280U
#define TFT_LCD_H 240U
#endif
#ifndef TFT_SPI_UNIT
#define TFT_SPI_UNIT hspi1
#endif
#if defined(LCD_CS_GPIO_Port) && defined(LCD_CS_Pin)
#define TFT_CS_GPIO_Port LCD_CS_GPIO_Port
#define TFT_CS_Pin LCD_CS_Pin
#else
#define TFT_CS_GPIO_Port cs2_GPIO_Port
#define TFT_CS_Pin cs2_Pin
#endif
#if defined(LCD_BLK_GPIO_Port) && defined(LCD_BLK_Pin)
#define TFT_BLK_GPIO_Port LCD_BLK_GPIO_Port
#define TFT_BLK_Pin LCD_BLK_Pin
#else
#define TFT_BLK_GPIO_Port cs1_GPIO_Port
#define TFT_BLK_Pin cs1_Pin
#endif
#if defined(LCD_RES_GPIO_Port) && defined(LCD_RES_Pin)
#define TFT_RES_GPIO_Port LCD_RES_GPIO_Port
#define TFT_RES_Pin LCD_RES_Pin
#else
#define TFT_RES_GPIO_Port cs4_GPIO_Port
#define TFT_RES_Pin cs4_Pin
#endif
#if defined(LCD_DC_GPIO_Port) && defined(LCD_DC_Pin)
#define TFT_DC_GPIO_Port LCD_DC_GPIO_Port
#define TFT_DC_Pin LCD_DC_Pin
#else
#define TFT_DC_GPIO_Port cs3_GPIO_Port
#define TFT_DC_Pin cs3_Pin
#endif
#define TFT_COLOR_WHITE 0xFFFFU
#define TFT_COLOR_BLACK 0x0000U
#define TFT_COLOR_BLUE 0x001FU
#define TFT_COLOR_RED 0xF800U
#define TFT_COLOR_GREEN 0x07E0U
#define TFT_COLOR_CYAN 0x7FFFU
#define TFT_COLOR_YELLOW 0xFFE0U
#define TFT_COLOR_MAGENTA 0xF81FU
#define TFT_COLOR_DARK_GRAY 0x4208U
#define TFT_COLOR_LIGHT_GRAY 0xC618U
void TFT_Init(void);
void TFT_Clear(uint16_t color);
void TFT_FillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint16_t color);
void TFT_DrawPoint(uint16_t x, uint16_t y, uint16_t color);
void TFT_DrawString(uint16_t x, uint16_t y, const char *text, uint16_t fc, uint16_t bc, uint8_t scale);
void TFT_ShowStatus(float temperature_c, RobotMode_t mode);
const char *TFT_RobotModeText(RobotMode_t mode);
void LCD_Init(void);
void LCD_Address_Set(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
void LCD_Fill(uint16_t xsta, uint16_t ysta, uint16_t xend, uint16_t yend, uint16_t color);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -0,0 +1,366 @@
#include "w25q64.h"
#include <string.h>
static W25Q64_Handle_t w25q64 = {0};
static bool w25q64_attached = false;
static bool w25q64_ready = false;
static void w25q64_cs_low(void)
{
if (w25q64_attached)
{
HAL_GPIO_WritePin(w25q64.cs_port, w25q64.cs_pin, GPIO_PIN_RESET);
}
}
static void w25q64_cs_high(void)
{
if (w25q64_attached)
{
HAL_GPIO_WritePin(w25q64.cs_port, w25q64.cs_pin, GPIO_PIN_SET);
}
}
static W25Q64_Status_t w25q64_spi_tx(const uint8_t *data, uint16_t len)
{
if (!w25q64_attached || w25q64.hspi == NULL)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (HAL_SPI_Transmit(w25q64.hspi, (uint8_t *) data, len, w25q64.timeout_ms) != HAL_OK)
{
return W25Q64_ERR_HAL;
}
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_spi_rx(uint8_t *data, uint16_t len)
{
if (!w25q64_attached || w25q64.hspi == NULL)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (HAL_SPI_Receive(w25q64.hspi, data, len, w25q64.timeout_ms) != HAL_OK)
{
return W25Q64_ERR_HAL;
}
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_write_enable(void)
{
uint8_t cmd = W25Q64_CMD_WRITE_ENABLE;
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_read_status(uint8_t *status)
{
uint8_t cmd = W25Q64_CMD_READ_STATUS_1;
if (status == NULL)
{
return W25Q64_ERR_PARAM;
}
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
if (w25q64_spi_rx(status, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_wait_ready(uint32_t timeout_ms)
{
uint8_t status = 0;
uint32_t start = HAL_GetTick();
do
{
if (w25q64_read_status(&status) != W25Q64_OK)
{
return W25Q64_ERR_HAL;
}
if ((status & 0x01U) == 0U)
{
return W25Q64_OK;
}
} while ((HAL_GetTick() - start) < timeout_ms);
return W25Q64_ERR_BUSY;
}
static W25Q64_Status_t w25q64_command_addr(uint8_t cmd, uint32_t addr)
{
uint8_t header[4] = {
cmd,
(uint8_t) (addr >> 16),
(uint8_t) (addr >> 8),
(uint8_t) addr
};
w25q64_cs_low();
if (w25q64_spi_tx(header, sizeof(header)) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
return W25Q64_OK;
}
void W25Q64_Attach(W25Q64_Handle_t *handle)
{
if (handle == NULL)
{
W25Q64_Detach();
return;
}
w25q64 = *handle;
w25q64_attached = (w25q64.hspi != NULL) && (w25q64.cs_port != NULL);
w25q64.timeout_ms = (w25q64.timeout_ms == 0U) ? 100U : w25q64.timeout_ms;
if (w25q64_attached)
{
HAL_GPIO_WritePin(w25q64.cs_port, w25q64.cs_pin, GPIO_PIN_SET);
}
w25q64_ready = false;
}
void W25Q64_Detach(void)
{
memset(&w25q64, 0, sizeof(w25q64));
w25q64_attached = false;
w25q64_ready = false;
}
W25Q64_Status_t W25Q64_ReadID(uint32_t *jedec_id)
{
uint8_t cmd = W25Q64_CMD_READ_ID;
uint8_t id[3] = {0};
if (jedec_id == NULL)
{
return W25Q64_ERR_PARAM;
}
if (!w25q64_attached)
{
return W25Q64_ERR_NOT_ATTACHED;
}
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
if (w25q64_spi_rx(id, 3U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
*jedec_id = ((uint32_t) id[0] << 16) | ((uint32_t) id[1] << 8) | id[2];
return W25Q64_OK;
}
W25Q64_Status_t W25Q64_Init(void)
{
uint32_t jedec_id = 0;
if (!w25q64_attached)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (W25Q64_ReadID(&jedec_id) != W25Q64_OK)
{
w25q64_ready = false;
return W25Q64_ERR_HAL;
}
w25q64_ready = (jedec_id == W25Q64_JEDEC_ID);
return w25q64_ready ? W25Q64_OK : W25Q64_ERR_ID;
}
W25Q64_Status_t W25Q64_Read(uint32_t addr, uint8_t *data, uint32_t len)
{
W25Q64_Status_t ret;
uint32_t offset = 0U;
if (data == NULL || len == 0U)
{
return W25Q64_ERR_PARAM;
}
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
ret = w25q64_command_addr(W25Q64_CMD_READ_DATA, addr);
if (ret != W25Q64_OK)
{
return ret;
}
while (offset < len)
{
uint16_t chunk = (uint16_t) ((len - offset) > UINT16_MAX ? UINT16_MAX : (len - offset));
ret = w25q64_spi_rx(data + offset, chunk);
if (ret != W25Q64_OK)
{
w25q64_cs_high();
return ret;
}
offset += chunk;
}
w25q64_cs_high();
return W25Q64_OK;
}
static W25Q64_Status_t w25q64_page_program(uint32_t addr, const uint8_t *data, uint16_t len)
{
W25Q64_Status_t ret;
ret = w25q64_write_enable();
if (ret != W25Q64_OK)
{
return ret;
}
ret = w25q64_command_addr(W25Q64_CMD_PAGE_PROGRAM, addr);
if (ret != W25Q64_OK)
{
return ret;
}
if (w25q64_spi_tx(data, len) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return w25q64_wait_ready(w25q64.timeout_ms);
}
W25Q64_Status_t W25Q64_Write(uint32_t addr, const uint8_t *data, uint32_t len)
{
uint32_t offset = 0U;
if (data == NULL || len == 0U)
{
return W25Q64_ERR_PARAM;
}
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
while (offset < len)
{
uint32_t page_offset = (addr + offset) % W25Q64_PAGE_SIZE;
uint32_t room = W25Q64_PAGE_SIZE - page_offset;
uint16_t chunk = (uint16_t) ((len - offset) < room ? (len - offset) : room);
W25Q64_Status_t ret = w25q64_page_program(addr + offset, data + offset, chunk);
if (ret != W25Q64_OK)
{
return ret;
}
offset += chunk;
}
return W25Q64_OK;
}
W25Q64_Status_t W25Q64_EraseSector(uint32_t addr)
{
W25Q64_Status_t ret;
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
ret = w25q64_write_enable();
if (ret != W25Q64_OK)
{
return ret;
}
ret = w25q64_command_addr(W25Q64_CMD_SECTOR_ERASE, addr);
if (ret != W25Q64_OK)
{
return ret;
}
w25q64_cs_high();
return w25q64_wait_ready(400U);
}
W25Q64_Status_t W25Q64_Erase64K(uint32_t addr)
{
W25Q64_Status_t ret;
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
ret = w25q64_write_enable();
if (ret != W25Q64_OK)
{
return ret;
}
ret = w25q64_command_addr(W25Q64_CMD_BLOCK_ERASE_64K, addr);
if (ret != W25Q64_OK)
{
return ret;
}
w25q64_cs_high();
return w25q64_wait_ready(2000U);
}
W25Q64_Status_t W25Q64_ChipErase(void)
{
uint8_t cmd = W25Q64_CMD_CHIP_ERASE;
if (!w25q64_ready)
{
return W25Q64_ERR_NOT_ATTACHED;
}
if (w25q64_write_enable() != W25Q64_OK)
{
return W25Q64_ERR_HAL;
}
w25q64_cs_low();
if (w25q64_spi_tx(&cmd, 1U) != W25Q64_OK)
{
w25q64_cs_high();
return W25Q64_ERR_HAL;
}
w25q64_cs_high();
return w25q64_wait_ready(100000U);
}

View File

@@ -0,0 +1,59 @@
#ifndef W25Q64_H
#define W25Q64_H
#include "main.h"
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define W25Q64_PAGE_SIZE 256U
#define W25Q64_SECTOR_SIZE 4096U
#define W25Q64_FLASH_SIZE 0x800000U
#define W25Q64_JEDEC_ID 0xEF4017U
#define W25Q64_CMD_WRITE_ENABLE 0x06U
#define W25Q64_CMD_READ_STATUS_1 0x05U
#define W25Q64_CMD_READ_ID 0x9FU
#define W25Q64_CMD_READ_DATA 0x03U
#define W25Q64_CMD_PAGE_PROGRAM 0x02U
#define W25Q64_CMD_SECTOR_ERASE 0x20U
#define W25Q64_CMD_BLOCK_ERASE_64K 0xD8U
#define W25Q64_CMD_CHIP_ERASE 0xC7U
typedef enum
{
W25Q64_OK = 0,
W25Q64_ERR_PARAM = -1,
W25Q64_ERR_NOT_ATTACHED = -2,
W25Q64_ERR_HAL = -3,
W25Q64_ERR_ID = -4,
W25Q64_ERR_BUSY = -5
} W25Q64_Status_t;
typedef struct
{
SPI_HandleTypeDef *hspi;
GPIO_TypeDef *cs_port;
uint16_t cs_pin;
uint32_t timeout_ms;
} W25Q64_Handle_t;
void W25Q64_Attach(W25Q64_Handle_t *handle);
void W25Q64_Detach(void);
W25Q64_Status_t W25Q64_Init(void);
W25Q64_Status_t W25Q64_ReadID(uint32_t *jedec_id);
W25Q64_Status_t W25Q64_Read(uint32_t addr, uint8_t *data, uint32_t len);
W25Q64_Status_t W25Q64_Write(uint32_t addr, const uint8_t *data, uint32_t len);
W25Q64_Status_t W25Q64_EraseSector(uint32_t addr);
W25Q64_Status_t W25Q64_Erase64K(uint32_t addr);
W25Q64_Status_t W25Q64_ChipErase(void);
#ifdef __cplusplus
}
#endif
#endif

BIN
tmp/pdfs/dm_manual.pdf Normal file

Binary file not shown.