LCD 主要代码
//LCD端口定义
#define LCD_RST_CLR() (GPIOB_ResetBits(LCD_RST)) //RST
#define LCD_RST_SET() (GPIOB_SetBits(LCD_RST))
#define LCD_DC_CLR() (GPIOB_ResetBits(LCD_DC)) //dc for choose cmd or data
#define LCD_DC_SET() (GPIOB_SetBits(LCD_DC)) // Set is data
#define LCD_CS_CLR() (GPIOB_ResetBits(LCD_CS)) //CS, low is enable
#define LCD_CS_SET() (GPIOB_SetBits(LCD_CS)) //disable
#define LCD_BLK_CLR() (GPIOB_ResetBits(LCD_BLK)) //BLK, BLK must on
#define LCD_BLK_SET() (GPIOB_SetBits(LCD_BLK))
// LCD初始化
void TFT_LCD_Init(void)
{
LCD_SPI_Init();
LCD_CS_CLR();
LCD_RESET();
// ST7789 init 按手册配置初始化命令
LCD_WR_REG(0x11);
LCD_WR_REG(0x36);
if (USE_HORIZONTAL == 0) LCD_WR_DATA(0x00);
else if (USE_HORIZONTAL == 1) LCD_WR_DATA(0xC0);
else if (USE_HORIZONTAL == 2) LCD_WR_DATA(0x70);
else LCD_WR_DATA(0xA0);
LCD_WR_REG(0x3A);
LCD_WR_DATA(0x05);
LCD_WR_REG(0xB2);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x0C);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x33);
LCD_WR_REG(0xB7);
LCD_WR_DATA(0x35);
LCD_WR_REG(0xBB);
LCD_WR_DATA(0x35);
LCD_WR_REG(0xC0);
LCD_WR_DATA(0x2C);
LCD_WR_REG(0xC2);
LCD_WR_DATA(0x01);
LCD_WR_REG(0xC3);
LCD_WR_DATA(0x13);
LCD_WR_REG(0xC4);
LCD_WR_DATA(0x20);
LCD_WR_REG(0xC6);
LCD_WR_DATA(0x0F);
LCD_WR_REG(0xD0);
LCD_WR_DATA(0xA4);
LCD_WR_DATA(0xA1);
LCD_WR_REG(0xD6);
LCD_WR_DATA(0xA1);
LCD_WR_REG(0xE6);
LCD_WR_DATA(0xF0);
LCD_WR_DATA(0x00);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x04);
LCD_WR_DATA(0x05);
LCD_WR_DATA(0x29);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x3E);
LCD_WR_DATA(0x38);
LCD_WR_DATA(0x12);
LCD_WR_DATA(0x12);
LCD_WR_DATA(0x28);
LCD_WR_DATA(0x30);
LCD_WR_REG(0xE1);
LCD_WR_DATA(0xF0);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x0A);
LCD_WR_DATA(0x0D);
LCD_WR_DATA(0x0B);
LCD_WR_DATA(0x07);
LCD_WR_DATA(0x28);
LCD_WR_DATA(0x33);
LCD_WR_DATA(0x3E);
LCD_WR_DATA(0x36);
LCD_WR_DATA(0x14);
LCD_WR_DATA(0x14);
LCD_WR_DATA(0x29);
LCD_WR_DATA(0x32);
LCD_WR_REG(0x21);
LCD_WR_REG(0x11);
DelayMs(120);
LCD_WR_REG(0x29);
LCD_CS_SET();
}
// LCD显示图片
void LCD_Draw_Image(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t* pic)
{
uint16_t xend = x + width;
uint16_t yend = y + height;
uint32_t count = (uint32_t)width * height;
uint32_t i;
LCD_SetWindows(x, y, xend - 1, yend - 1); //显示范围
LCD_CS_CLR(); // 拉低片选
LCD_RST_SET();
for (i = 0; i < count; i++)
{
LCD_WriteByte(*pic++);
LCD_WriteByte(*pic++); //每个像素占用两字节
}
LCD_CS_SET();
}
flash W25Q 主要代码
// W25Q初始化
void W25Q_Init()
{
GPIOA_ModeCfg(W25Q_CS_PIN , GPIO_ModeOut_PP_5mA);
W25Q_CS_SET();
}
//W25Q全部擦除函数
void W25Q_EraseAll()
{
W25Q_WaitForWriteEnd();
W25Q_WriteEnable();
W25Q_CS_RESET();
W25Q_WriteByte(W25Q_CMD_CHIP_ERASE);
W25Q_CS_SET();
W25Q_WaitForWriteEnd();
}
//===================
//addr:起始页地址,必须地址对齐
//len:要写入的数据的字节数
//buf:要写入的数据所在的缓冲区地址
//返回值:此次写入占用页的数量
unsigned int W25Q_MemWrite(unsigned long addr, unsigned long len, const unsigned char *buf)
{
unsigned int pagenum1, pagenum2, var = 0;
pagenum1 = len >> 8;//计算页的数量(等效于len / 256)
pagenum2 = len & 255;//计算不足一页的字节数(等效于len % 256)
for (var = 0; var < pagenum1; ++var)
{
W25Q_PageWrite(addr + (var << 8), buf + var * 256, 256);//(等效于var * 256)
}
if(pagenum2)
{
W25Q_PageWrite(addr + pagenum1 * 256, buf + pagenum1 * 256, pagenum2);//不足一页部分占用一页
return pagenum1 + 1;
}
else
{
return pagenum1;
}
}
void W25Q_PageWrite(unsigned long addr, const unsigned char *buf, unsigned int len)
{
//============================【按页写入】================================
//参数:
//addr:页地址,必须地址对齐
//buf:要写入的数据所在的缓冲区地址,缓冲区大小不能超过256
//=======================================================================
if (len > 256)
len = 256;
W25Q_WaitForWriteEnd();
W25Q_WriteEnable();
W25Q_CS_RESET();
W25Q_WriteByte(W25Q_CMD_PAGE_PROGRAM);
W25Q_WriteByte((addr & 0xFF0000) >> 16);
W25Q_WriteByte((addr & 0x00FF00) >> 8);
W25Q_WriteByte(addr & 0x000000);//强制地址256对齐
while (len--)
{
W25Q_WriteByte(*buf++);
}
W25Q_CS_SET();
W25Q_WaitForWriteEnd();
}
//按字节写入flash
void W25Q_WriteByte(uint8_t data)
{
#if SPI_TYPE == SPI_HARDWARE
// GPIOA_ModeCfg(SPI_SCLK_PIN | SPI_MISO_PIN, GPIO_ModeOut_PP_5mA);
// SPI_2WIRE_MasterOutputInit();
// R8_SPI0_CTRL_MOD |= RB_SPI_MISO_OE;//MISO 输出使能,用作两线SPI模式下的SPI输出
SPI0_MasterSendByte(data);
#else
for (uint8_t i = 0; i < 8; i ++)//循环8次,依次交换每一位数据
{
if(data & 0x80)
{
GPIOA_SetBits(SPI_MOSI_PIN);
}
else
{
GPIOA_ResetBits(SPI_MOSI_PIN);
}
DelayUs(4);
data <<= 1;
GPIOA_SetBits(SPI_SCLK_PIN);//SCK上升沿发送MOSI数据,主机提前把数据放在MOSI线上,拉高时钟线让从机来读取
DelayUs(4);
// DelayUs(2);
GPIOA_ResetBits(SPI_SCLK_PIN);//SCK上升沿发送MOSI数据,主机提前把数据放在MOSI线上,拉高时钟线让从机来读取
DelayUs(4);
// DelayUs(2);
}
#endif
}
void W25Q_DMAMemRead(unsigned long addr, unsigned long len, unsigned char *buf)
{
//============================【不定长连续DMA读】================================
//参数:
//addr:起始页地址
//len:要读出的数据的字节数
//buf:存储读出数据的缓冲区地址
//==========================================================================
unsigned long remaining = len;
unsigned long offset = 0;
W25Q_WaitForWriteEnd();
GPIOB_SetBits(LCD_CS);
W25Q_CS_RESET();
W25Q_WriteByte(W25Q_CMD_READ_DATA);
W25Q_WriteByte((addr & 0xFF0000) >> 16);
W25Q_WriteByte((addr & 0x00FF00) >> 8);
W25Q_WriteByte(addr & 0x0000FF) ;
MCU_MOSI_DIABLE(); // 失能mcu MOSI
GPIOB_SetBits(LCD_RST);
GPIOB_ResetBits(LCD_CS); //注意此时必须拉低LCD片选
while(remaining > 0) /*DMA传输一次最大字节为4095,图片数据远超最大字节,需要分次传输*/
{
unsigned long chunk = (remaining > 4095) ? 4095 : remaining;
SPI0_MasterDMARecv(buf + offset, chunk);
remaining -= chunk;
offset += chunk;
}
//SPI0_MasterDMARecv(buf, len);
W25Q_CS_SET();
MCU_MOSI_ENABLE();
}
main函数实现
__attribute__((aligned(4))) uint8_t ReadBuf[1200];
void DebugInit(void) // SPI0与例程惯用串口打印引脚冲突,所以此例程改用UART1_输出打印
{
GPIOB_SetBits(GPIO_Pin_13);
GPIOPinRemap(ENABLE, RB_PIN_UART1);
GPIOB_ModeCfg(GPIO_Pin_12, GPIO_ModeIN_PU);
GPIOB_ModeCfg(GPIO_Pin_13, GPIO_ModeOut_PP_5mA);
UART1_DefInit();
}
// 图片页对齐,必须根据flash页大小对齐地址
#define PAGE_SIZE 256
#define ALIGN_PAGE(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
// 定义图片存储地址
#define IMG1_ADDR 0x00000000
#define IMG2_ADDR ALIGN_PAGE(IMG1_ADDR + sizeof(gImage_001))
#define IMG3_ADDR ALIGN_PAGE(IMG2_ADDR + sizeof(gImage_002))
// 图片数组,用于循环显示
const uint8_t *img_ptr[] = {gImage_001, gImage_002, gImage_003};
const uint32_t img_addr[] = {IMG1_ADDR, IMG2_ADDR, IMG3_ADDR,IMG4_ADDR};
const uint32_t img_size[] = {sizeof(gImage_001), sizeof(gImage_002), sizeof(gImage_003), sizeof(gImage_004)};
const uint8_t img_count = 4;
/*********************************************************************
* @fn main
*
* @brief 主函数
*
* @return none
*/
__HIGH_CODE
__attribute__((noinline))
int main()
{
uint8_t i;
uint32_t Flash_ID;
uint32_t pages, addr;
HSECFG_Capacitance(HSECap_18p);
SetSysClock(SYSCLK_FREQ);
/* 配置串口调试 */
DebugInit();
PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);
#if SPI_TYPE == SPI_HARDWARE
BSP_Hard_SPI_Init();
#else
BSP_Soft_SPI_Init();
#endif
TFT_LCD_Init(); //LCD初始化
LCD_Fill(0, 0, LCD_W, LCD_H, LCD_WHITE); //背景色填充
W25Q_Init(); //flash初始化
W25Q_EraseAll(); //flash擦除
LCD_Draw_Image(10,10,200, 200, gImage_001);
W25Q_MemWrite(IMG1_ADDR, sizeof(gImage_001), gImage_001); //向flash中写入图片数据
W25Q_MemWrite(IMG2_ADDR, sizeof(gImage_002), gImage_002);
W25Q_MemWrite(IMG3_ADDR, sizeof(gImage_003), gImage_003);
uint8_t current_img = 0;
while (1) {
GPIOB_SetBits(LCD_DC);
W25Q_DMAMemRead(img_addr[current_img], img_size[current_img], ReadBuf); //循环显示flash中的图片,ReadBuf为空
current_img = (current_img + 1) % img_count;
DelayMs(10); //注意调整延时时间,否则图片显示乱码
}
}