diff --git a/User_Code/module/periph/tft/tft.c b/User_Code/module/periph/tft/tft.c new file mode 100644 index 0000000..5293dfe --- /dev/null +++ b/User_Code/module/periph/tft/tft.c @@ -0,0 +1,552 @@ +#include "tft.h" +#include +#include +#include +#include + +#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(®, 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; + } +} diff --git a/User_Code/module/periph/tft/tft.h b/User_Code/module/periph/tft/tft.h new file mode 100644 index 0000000..64fe26b --- /dev/null +++ b/User_Code/module/periph/tft/tft.h @@ -0,0 +1,86 @@ +#ifndef TFT_H +#define TFT_H + +#include "main.h" +#include "robot_def.h" +#include "spi.h" +#include + +#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 diff --git a/User_Code/module/periph/w25q64/w25q64.c b/User_Code/module/periph/w25q64/w25q64.c new file mode 100644 index 0000000..d91e496 --- /dev/null +++ b/User_Code/module/periph/w25q64/w25q64.c @@ -0,0 +1,366 @@ +#include "w25q64.h" +#include + +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); +} diff --git a/User_Code/module/periph/w25q64/w25q64.h b/User_Code/module/periph/w25q64/w25q64.h new file mode 100644 index 0000000..9f74881 --- /dev/null +++ b/User_Code/module/periph/w25q64/w25q64.h @@ -0,0 +1,59 @@ +#ifndef W25Q64_H +#define W25Q64_H + +#include "main.h" +#include +#include + +#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 diff --git a/tmp/pdfs/dm_manual.pdf b/tmp/pdfs/dm_manual.pdf new file mode 100644 index 0000000..2f1a79f Binary files /dev/null and b/tmp/pdfs/dm_manual.pdf differ