58-DMA-SPI驱动显示屏,STC32G144K246实验箱演示程序
58-DMA-SPI驱动显示屏,STC32G144K246实验箱演示程序DMA-SPI驱动1.3寸TFT显示屏-ST7789-240x240,主程序main.C代码如下(汇编版本在附件中):
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/
/*************功能说明 **************
本例程基于STC32G144K246为主控芯片的实验箱进行编写测试。
使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
本实验测试使用的是1.3寸240*240 SPI接口驱动的彩屏
通过DMA硬件自动将xdata空间数据传输给SPI接口,在LCD屏幕显示。
下载时, 选择IRC频率 48MHz,主时钟使用 PLL 90MHz 时钟频率.
******************************************/
#include "tft.h"
#include "pic.h"
/****************用户定义宏****************/
/****************本地常量声明**************/
/****************本地变量声明**************/
/****************本地函数声明**************/
void HPLL_config(void);
void SPI_init(void);
void DMA_Config(void);
/******************** 主函数 **************************/
void main(void)
{
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 1; //避免 DMA 与 CPU 同时访问 xdata 时产生总线冲突
HPLL_config();
SPI_Init();
LCD_Init();
DMA_Config();
EA = 1; //允许总中断
LCD_Fill(0,0,240,240,LGRAY);
TFT_ShowStart();
while (1);
}
//========================================================================
// 函数: void HPLL_config(void)
// 描述: PLL时钟配置函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-11-01
// 备注:
//========================================================================
void HPLL_config(void)
{
#if(HPLL_SEL==SEL_HPLL2)
WTST = 2; //通过WTST增加等待时钟控制Flash读取速度在33MHz以内, 主时钟:360MHz/2/2=90MHz, 读Flash速度:90MHz/(1+2)=30MHz
HPLLCR |= 0x80; //使能HPLL(高速外设默认使用HPLL时钟,切换到HPLL2前要先使能HPLL,切换完再关闭HPLL)
//首先需要将HIRC主频调节到48MHz
HPLL2CR &= ~0x10; //选择HPLL2输入时钟源为HIRC
// HPLL2CR |= 0x10; //选择HPLL2输入时钟源为IRCM
HPLL2PDIV = 8; //设置HPLL2输入时钟预分频为8(HPLL输入频率必须为6MHz)
// HPLL2CR |= 0x00; //HPLL2=6MHz*52=312MHz
// HPLL2CR |= 0x01; //HPLL2=6MHz*54=324MHz
// HPLL2CR |= 0x02; //HPLL2=6MHz*56=336MHz
// HPLL2CR |= 0x03; //HPLL2=6MHz*58=348MHz
HPLL2CR |= 0x04; //HPLL2=6MHz*60=360MHz
// HPLL2CR |= 0x05; //HPLL2=6MHz*62=372MHz
// HPLL2CR |= 0x06; //HPLL2=6MHz*64=384MHz
// HPLL2CR |= 0x07; //HPLL2=6MHz*66=396MHz
// HPLL2CR |= 0x08; //HPLL2=6MHz*68=408MHz
// HPLL2CR |= 0x09; //HPLL2=6MHz*70=420MHz
// HPLL2CR |= 0x0a; //HPLL2=6MHz*72=432MHz
// HPLL2CR |= 0x0b; //HPLL2=6MHz*74=444MHz
// HPLL2CR |= 0x0c; //HPLL2=6MHz*76=456MHz
// HPLL2CR |= 0x0d; //HPLL2=6MHz*78=468MHz
// HPLL2CR |= 0x0e; //HPLL2=6MHz*80=480MHz
// HPLL2CR |= 0x0f; //HPLL2=6MHz*82=492MHz
HPLL2CR |= 0x20; //高速外设时钟选择 HPLL2/2
HPLL2CR |= 0x80; //使能HPLL2
HPLLCR &= ~0x80; //关闭HPLL(高速外设默认使用HPLL时钟,切换到HPLL2前要先使能HPLL,切换完再关闭HPLL)
CLKDIV = 2; //系统时钟 = 主时钟源/2 = HPLL2/2/2 = 90MHz
CLKSEL = 0x08; //选择HPLL2/2作为主时钟源
#else
WTST = 3; //通过WTST增加等待时钟控制Flash读取速度在33MHz以内, 主时钟:480MHz/2/2=120MHz, 读Flash速度:120MHz/(1+3)=30MHz
//首先需要将HIRC主频调节到48MHz
HPLLCR &= ~0x10; //选择HPLL输入时钟源为HIRC
// HPLLCR |= 0x10; //选择HPLL输入时钟源为IRCM
HPLLPDIV = 8; //设置HPLL输入时钟预分频为8(HPLL输入频率必须为6MHz)
// HPLLCR |= 0x00; //HPLL=6MHz*52=312MHz
// HPLLCR |= 0x01; //HPLL=6MHz*54=324MHz
// HPLLCR |= 0x02; //HPLL=6MHz*56=336MHz
// HPLLCR |= 0x03; //HPLL=6MHz*58=348MHz
// HPLLCR |= 0x04; //HPLL=6MHz*60=360MHz
// HPLLCR |= 0x05; //HPLL=6MHz*62=372MHz
// HPLLCR |= 0x06; //HPLL=6MHz*64=384MHz
// HPLLCR |= 0x07; //HPLL=6MHz*66=396MHz
// HPLLCR |= 0x08; //HPLL=6MHz*68=408MHz
// HPLLCR |= 0x09; //HPLL=6MHz*70=420MHz
// HPLLCR |= 0x0a; //HPLL=6MHz*72=432MHz
// HPLLCR |= 0x0b; //HPLL=6MHz*74=444MHz
// HPLLCR |= 0x0c; //HPLL=6MHz*76=456MHz
// HPLLCR |= 0x0d; //HPLL=6MHz*78=468MHz
HPLLCR |= 0x0e; //HPLL=6MHz*80=480MHz
// HPLLCR |= 0x0f; //HPLL=6MHz*82=492MHz
HPLL2CR &= ~0x60; //高速外设时钟选择 HPLL/2
HPLLCR |= 0x80; //使能HPLL
CLKDIV = 2; //系统时钟=主时钟源/2 = HPLL/2/2 = 120MHz
CLKSEL = 0x04; //选择HPLL/2作为主时钟源
#endif
// P5M0 |= 0x10; P5M1 &= ~0x10;//设置P5.4口为推挽输出
// P5SR &= ~0x10; //设置P5.4口为快速模式
// HIRCCR |= 0x10; //输出系统时钟
// MCLKOCR = 10; //系统时钟 10 分频到P5.4
}
//========================================================================
// 函数: void DMA_Config(void)
// 描述: SPI DMA 功能配置.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2021-5-6
//========================================================================
void DMA_Config(void)
{
DMA_SPI_STA = 0x00;
DMA_SPI_CFG = 0xE0; //bit7 1:Enable Interrupt
DMA_SPI_AMTH = 0xfd; //设置传输总字节数:n+1
DMA_SPI_AMT = 0x1f; //设置传输总字节数:n+1
DMA_SPI_TXAH = (u8)((u16)&IMG_DATA >> 8); //SPI发送数据存储地址
DMA_SPI_TXAL = (u8)((u16)&IMG_DATA);
DMA_SPI_ITVH = 0;
DMA_SPI_ITVL = 0;
DMA_SPI_CFG2 = 0x01; //01:P2.2
DMA_SPI_CR = 0x81; //bit7 1:使能 SPI_DMA, bit6 1:开始 SPI_DMA 主机模式, bit0 1:清除 SPI_DMA FIFO
}
//========================================================================
// 函数: void SPI_DMA_Interrupt (void) interrupt 49
// 描述: SPI DMA中断函数
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2021-5-8
// 备注:
//========================================================================
void SPI_DMA_Interrupt(void) interrupt DMA_SPI_VECTOR
{
DMA_SPI_STA = 0;
TFT_ShowEnd();
}
58-DMA-SPI驱动显示屏,STC32G144K246实验箱演示程序
DMA-SPI刷新OLED12864显示屏程序,SSD1306驱动,0.96寸,主程序main.C代码如下(汇编版本在附件中):
/*---------------------------------------------------------------------*/
/* --- Web: www.STCAI.com ---------------------------------------------*/
/* --- BBS: www.STCAIMCU.com-----------------------------------------*/
/*---------------------------------------------------------------------*/
/*************功能说明 **************
本例程基于STC32G144K246为主控芯片的实验箱进行编写测试。
使用Keil C251编译器,Memory Model推荐设置XSmall模式,默认定义变量在edata,单时钟存取访问速度快。
edata建议保留1K给堆栈使用,空间不够时可将大数组、不常用变量加xdata关键字定义到xdata空间。
单色OLED12864显示屏驱动程序,驱动IC为SSD1306,SPI接口,通过SPI DMA将1024字节的图片数据送到彩屏,传送时不占用CPU时间。
显示图形,汉字,英文,数字.
其中图形显示发送命令和图片数据使用SPI DMA操作,传输数据时不占用CPU时间。做GUI最方便了,可以先操作定义于xdata的1024字节缓存,然后触发SPI DMA即可,523us或943us即可自己动刷完。
本例运行于40MHz, SPI速度为主频4分频(10MHz),每次SPI DMA传输总时间943us,SPI速度为主频2分频(20MHz),每次SPI DMA传输总时间523us。
将要显示的内容放在1024字节的显存中,启动DMA传输即可。
下载时, 选择IRC频率 48MHz,主时钟使用 PLL 90MHz 时钟频率.
******************************************/
#include "..\..\comm\STC32G144K246.h"
#include "ASCII6x8.h"
#include "HZK16.h"
#include "ASCII-10x24.h"
#include "picture1.h"
#include "picture2.h"
#define u8 unsigned char
#define u16 unsigned int
#define u32 unsigned long
/****************用户定义宏****************/
#define SEL_HPLL1 1
#define SEL_HPLL2 2
#define HPLL_SEL SEL_HPLL2
#if(HPLL_SEL==SEL_HPLL1)
#define MAIN_Fosc 120000000L //定义主时钟(超频)
#else
#define MAIN_Fosc 90000000L//定义主时钟
#endif
/****************本地引脚定义**************/
/* 定义接口 */
//GND STC32G144K246实验箱
//VCC 3~5V
sbit P_OLED_CLK = P2^5; //D0 SPI or II2 的时钟脚
sbit P_OLED_DIN = P2^3; //D1 SPI or II2 的数据脚
sbit P_OLED_RST = P2^4; //RES 复位脚, 低电平复位
sbit P_OLED_DC = P2^2; //DC 数据或命令脚
sbit P_OLED_CS = P2^6; //CS 片选脚
/****************本地变量声明**************/
u16 SPI_TxCnt; //SPI DMA触发发送次数, 一次128字节, 一共8次
bit B_SPI_DMA_busy; //SPI DMA忙标志, 1标志SPI-DMA忙,SPI DMA中断中清除此标志,使用SPI DMA前要确认此标志为0
u16 SPI_TxAddr; //SPI DMA要发送数据的首地址
bit B_TxCmd; //已发送命令标志
u8 xdata CmdTmp; //命令缓冲
u8 xdata DisTmp; //显示缓冲,将要显示的内容放在显存里,启动DMA即可. 由于LCM DMA有4字节对齐问题,所以这里定位对地址为4的倍数
/****************本地函数声明**************/
void HPLL_config(void);
//========================================================================
// 函数: voidSPI_Config(void)
// 描述: SPI初始化函数。
// 参数: none
// 返回: none.
// 版本: VER1.0
// 日期: 2024-8-13
// 备注:
//========================================================================
void SPI_Config(void)
{
SPI_S1 = 0; //00: P1.2 P1.3 P1.4 P1.5, 01: P2.2 P2.3 P2.4 P2.5, 10: P5.4 P4.0 P4.1 P4.2, 11: P3.5 P3.4 P3.3 P3.2
SPI_S0 = 1;
SSIG = 1; //1: 忽略SS脚,由MSTR位决定主机还是从机 0: SS脚用于决定主机还是从机。
SPEN = 1; //1: 允许SPI, 0: 禁止SPI,所有SPI管脚均为普通IO
DORD = 0; //1: LSB先发, 0: MSB先发
MSTR = 1; //1: 设为主机 0: 设为从机
CPOL = 1; //1: 空闲时SCLK为高电平, 0: 空闲时SCLK为低电平
CPHA = 1; //1: 数据在SCLK前沿驱动,后沿采样. 0: 数据在SCLK前沿采样,后沿驱动.
SPCTL = (SPCTL & ~3) | 0; //SPI 时钟频率选择, 0: 4T, 1: 8T,2: 16T,3: 2T
SPI_CLKDIV = 2; //SPI_CLKDIV时钟分频
HSSPI_CFG = 0;
HSSPI_CFG2 |= 0x20; //使能SPI高速模式
SPSTAT = 0x80 + 0x40; //清0 SPIF和WCOL标志
}
void LCD_delay_ms(u16 ms) // 1~65535
{
u16 i;
do
{
i = MAIN_Fosc / 6000;
while(--i) ;
}while(--ms);
}
//******************************************
void OLED_WriteData(u8 dat) //write display data to LCD
{
P_OLED_DC = 1;
P_OLED_CS = 0;
SPDAT = dat; //发送一个字节
while(SPIF == 0) ; //等待发送完成
SPSTAT = 0x80 + 0x40; //清0 SPIF和WCOL标志
P_OLED_CS = 1;
}
//******************************************
void OLED_WriteCMD(u8 cmd)
{
P_OLED_DC = 0;
P_OLED_CS = 0;
SPDAT = cmd; //发送一个字节
while(SPIF == 0) ; //等待发送完成
SPSTAT = 0x80 + 0x40; //清0 SPIF和WCOL标志
P_OLED_CS = 1;
P_OLED_DC = 1;
}
//========================================================================
// 函数: void Set_Dot_Addr_LCD(int x,int y)
// 描述: 设置在LCD的真实坐标系上的X、Y点对应的RAM地址
// 参数: x X轴坐标
// y Y轴坐标
// 返回: 无
// 备注: 仅设置当前操作地址,为后面的连续操作作好准备
// 版本:
// 2007/04/10 First version
//========================================================================
void Set_Dot_Addr(u8 x,u8 y)
{
OLED_WriteCMD((u8)(0xb0 + y)); //设置页0~7
OLED_WriteCMD((x >> 4)| 0x10); //设置列0~127 高nibble
OLED_WriteCMD(x & 0x0f); //设置列0~127 低nibble
}
//******************************************
void FillPage(u8 y,u8 color) //Clear Page LCD RAM
{
u8 j;
Set_Dot_Addr(0,y);
for(j=0; j<128; j++) OLED_WriteData(color);
}
//******************************************
void FillAll(u8 color) //Clear CSn LCD RAM
{
u8 i;
for(i=0; i<8; i++) FillPage(i,color);
}
//******************************************
void Initialize_OLED(void) //initialize OLED
{
P_OLED_CS= 1;
P_OLED_RST = 1;
P_OLED_DC= 1;
P_OLED_DIN = 1;
P_OLED_CLK = 1;
P_OLED_RST = 0;
LCD_delay_ms(100);
P_OLED_RST = 1;
LCD_delay_ms(100);
OLED_WriteCMD(0xAE); //Set Display Off
OLED_WriteCMD(0xd5); //display divide ratio/osc. freq. mode
OLED_WriteCMD(0x80); //
OLED_WriteCMD(0xA8); //multiplex ration mode:63
OLED_WriteCMD(0x3F);
OLED_WriteCMD(0xD3); //Set Display Offset
OLED_WriteCMD(0x00);
OLED_WriteCMD(0x40); //Set Display Start Line
OLED_WriteCMD(0x8D); //Set Display Offset
OLED_WriteCMD(0x14);
OLED_WriteCMD(0xA1); //Segment Remap
OLED_WriteCMD(0xC8); //Sst COM Output Scan Direction
OLED_WriteCMD(0xDA); //common pads hardware: alternative
OLED_WriteCMD(0x12);
OLED_WriteCMD(0x81); //contrast control
OLED_WriteCMD(0xCF);
OLED_WriteCMD(0xD9); //set pre-charge period
OLED_WriteCMD(0xF1);
OLED_WriteCMD(0xDB); //VCOM deselect level mode
OLED_WriteCMD(0x40); //set Vvcomh=0.83*Vcc
OLED_WriteCMD(0xA4); //Set Entire Display On/Off
OLED_WriteCMD(0xA6); //Set Normal Display
OLED_WriteCMD(0xAF); //Set Display On
FillAll(0);
}
//******************************************
void WriteAscii6x8(u8 x,u8 y, u8 number)
{
u8 const *p;
u8 i;
if(x > (128-5)) return;
if(y >= 8) return;
p = (u16)number * 6 + ASCII6x8;
Set_Dot_Addr(x,y);
for(i=0; i<6; i++)
{
OLED_WriteData(*p);
p++;
}
}
//=====================================================
void WriteHZ16(u8 x, u8 y, u16 hz) //向指定位置写一个汉字, x为横向的点0~127, y为纵向的页0~7, hz为要写的汉字.
{
u8 const *p;
u8 i;
if(x > (128-16)) return;
if(y > 6) return;
p = hz * 32 + HZK16;
Set_Dot_Addr(x, y);
for(i=0; i<16; i++) { OLED_WriteData(*p); p++;}
Set_Dot_Addr(x, (u8)(y+1));
for(i=0; i<16; i++) { OLED_WriteData(*p); p++;}
}
void printf_ASCII_text(u8 x, u8 y, u8 *ptr) //最快写入10个ASCII码(10*6+9=69个字节)耗时430us@24MHZ
{
u8c;
for (;;)
{
c = *ptr;
if(c == 0) return; //遇到停止符0结束
if(c < 0x80) //ASCII码
{
WriteAscii6x8(x,y,c);
x += 6;
}
ptr++;
}
}
//******************************************
void WriteAscii_10x24(u8 x, u8 y, u8 chr) //向指定位置写一个ASCII码字符, x为横向的点0~127, y为纵向的页0~7, chr为要写的字符
{
u8 const *p;
u8 i;
if(x > (128-10)) return;
if(y >= 6) return;
p = (u16)chr * 30 + ASCII10x24;
Set_Dot_Addr(x, y);
for(i=0; i<10; i++) { OLED_WriteData(*p); p++; }
Set_Dot_Addr(x, (u8)(y+1));
for(i=0; i<10; i++) { OLED_WriteData(*p); p++; }
Set_Dot_Addr(x, (u8)(y+2));
for(i=0; i<10; i++) { OLED_WriteData(*p); p++; }
}
//******************************************
void WriteDot_3x3(u8 x, u8 y) //向指定位置写一个小数点, x为横向的点0~127, y为纵向的页0~7
{
if(x > (128-3)) return;
if(y >= 8) return;
Set_Dot_Addr(x, y);
OLED_WriteData(0x38);
OLED_WriteData(0x38);
OLED_WriteData(0x38);
}
//************ 打印ASCII 10x24英文字符串 *************************
void printf_ascii_10x24(u8 x, u8 y, u8 const *ptr) //x为横向的点0~127, y为纵向的页0~7, *ptr为要打印的字符串指针, 间隔2点.
{
u8 c;
for (;;)
{
if(x > (128-10)) return;
if(y > 5) return;
c = *ptr;
if(c == 0) return; //遇到停止符0结束
if((c >= '0') && (c <= '9')) //ASCII码
{
WriteAscii_10x24(x,y,(u8)(c-'0'));
x += 12;
}
else if(c == '.')
{
WriteDot_3x3(x,(u8)(y+2));
x += 6;
}
else if(c == ' ') //显示空格
{
WriteAscii_10x24(x,y,11);
x += 12;
}
else if(c == '-') //显示空格
{
WriteAscii_10x24(x,y,10);
x += 12;
}
ptr++;
}
}
//DMA_SPI_CR SPI_DMA控制寄存器
#define DMA_ENSPI (1<<7) // SPI DMA功能使能控制位, bit7, 0:禁止SPI DMA功能,1:允许SPI DMA功能。
#define SPI_TRIG_M (1<<6) // SPI DMA主机模式触发控制位,bit6, 0:写0无效, 1:写1开始SPI DMA主机模式操作。
#define SPI_TRIG_S (0<<5) // SPI DMA从机模式触发控制位,bit5, 0:写0无效, 1:写1开始SPI DMA从机模式操作。
#define SPI_CLRFIFO 1 // 清除SPI DMA接收FIFO控制位,bit0, 0:写0无效, 1:写1复位FIFO指针。
//DMA_SPI_CFG SPI_DMA配置寄存器
#define DMA_SPIIE (1<<7) // SPI DMA中断使能控制位,bit7, 0:禁止SPI DMA中断, 1:允许中断。
#define SPI_ACT_TX (1<<6) // SPI DMA发送数据控制位,bit6, 0:禁止SPI DMA发送数据,主机只发时钟不发数据,从机也不发. 1:允许发送。
#define SPI_ACT_RX (0<<5) // SPI DMA接收数据控制位,bit5, 0:禁止SPI DMA接收数据,主机只发时钟不收数据,从机也不收. 1:允许接收。
#define DMA_SPIIP (0<<2) // SPI DMA中断优先级控制位,bit3~bit2, (最低)0~3(最高).
#define DMA_SPIPTY 0 // SPI DMA数据总线访问优先级控制位,bit1~bit0, (最低)0~3(最高).
//DMA_SPI_CFG2 SPI_DMA配置寄存器2
#define SPI_WRPSS (0<<2) // SPI DMA过程中使能SS脚控制位,bit2, 0: SPI DMA传输过程不自动控制SS脚。1:自动拉低SS脚。
#define SPI_SSS 0 // SPI DMA过程中自动控制SS脚选择位,bit1~bit0, 0: P1.4,1:P2.4,2: P4.0,3:P3.5。
//DMA_SPI_STA SPI_DMA状态寄存器
#define SPI_TXOVW (1<<2) // SPI DMA数据覆盖标志位,bit2, 软件清0.
#define SPI_RXLOSS (1<<1) // SPI DMA接收数据丢弃标志位,bit1, 软件清0.
#define DMA_SPIIF 1 // SPI DMA中断请求标志位,bit0, 软件清0.
//HSSPI_CFG高速SPI配置寄存器
#define SS_HOLD (0<<4) //高速模式时SS控制信号的HOLD时间, 0~15, 默认3. 在DMA中会增加N个系统时钟,当SPI速度为系统时钟/2时执行DMA,SS_HOLD、SS_SETUP和SS_DACT都必须设置大于2的值.
#define SS_SETUP 0 //高速模式时SS控制信号的SETUP时间,0~15, 默认3. 在DMA中不影响时间, 当SPI速度为系统时钟/2时执行DMA,SS_HOLD、SS_SETUP和SS_DACT都必须设置大于2的值.
//HSSPI_CFG2高速SPI配置寄存器2
#define SPI_IOSW (0<<6) //bit6:交换MOSI和MISO脚位,0:不交换,1:交换
#define HSSPIEN (0<<5) //bit5:高速SPI使能位,0:关闭高速模式,1:使能高速模式
#define FIFOEN (1<<4) //bit4:高速SPI的FIFO模式使能位,0:关闭FIFO模式,1:使能FIFO模式,使能FIFO模式在DMA中减少13个系统时间。
#define SS_DACT 3 //bit3~0:高速模式时SS控制信号的DEACTIVE时间,0~15, 默认3, 不影响DMA时间.当SPI速度为系统时钟/2时执行DMA,SS_HOLD、SS_SETUP和SS_DACT都必须设置大于2的值.
void SPI_DMA_TRIG(u8 xdata *TxBuf)
{
HSSPI_CFG= SS_HOLD | SS_SETUP; //SS_HOLD会增加N个系统时钟, SS_SETUP没有增加时钟。驱动OLED 40MHz时SS_HOLD可以设置为0,
HSSPI_CFG2 = SPI_IOSW | FIFOEN | SS_DACT; //FIFOEN允许FIFO会减小13个时钟.
SPI_TxAddr = (u16)TxBuf; //要发送数据的首地址
DMA_SPI_ITVH = 0;
DMA_SPI_ITVL = 0;
DMA_SPI_STA= 0;
DMA_SPI_CFG= DMA_SPIIE | SPI_ACT_TX | SPI_ACT_RX | DMA_SPIIP | DMA_SPIPTY;
DMA_SPI_CFG2 = SPI_WRPSS | SPI_SSS;
P_OLED_CS = 0;
B_TxCmd = 0; //已发送命令标志
SPI_TxCnt = 0; //SPI DMA触发发送次数, 一次128字节, 一共8次
B_SPI_DMA_busy = 1; //标志SPI-DMA忙,SPI DMA中断中清除此标志,使用SPI DMA前要确认此标志为0
DMA_SPI_STA = DMA_SPIIF; //软件触发SPI DMA中断,启动发送
}
//========================================================================
// 函数: void SPI_DMA_ISR (void) interrupt DMA_SPI_VECTOR
// 描述:SPI_DMA中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2024-1-5
//========================================================================
void SPI_DMA_ISR (void) interrupt DMA_SPI_VECTOR
{
if(SPI_TxCnt >= 8) //判断发送是否完毕.
{
DMA_SPI_CR = 0; //关闭SPI DMA
B_SPI_DMA_busy = 0; //清除SPI-DMA忙标志,SPI DMA中断中清除此标志,使用SPI DMA前要确认此标志为0
SPSTAT = 0x80 + 0x40; //清0 SPIF和WCOL标志
HSSPI_CFG2 = SPI_IOSW | SS_DACT; //使用SPI查询或中断方式时,要禁止FIFO
P_OLED_CS = 1;
}
else //仍有数据要发送
{
if(!B_TxCmd) //还没有发设置地址命令,则先发设置地址命令
{
B_TxCmd = 1; //指示已发地址命令
CmdTmp = (u8)(0xb0 + SPI_TxCnt);
CmdTmp = (u8)(((0>>4) & 0x0f) + 0x10); //设置列地址的高4 位
CmdTmp = 0 & 0x0f; //设置列地址的低4 位
P_OLED_DC = 0; //写命令
DMA_SPI_TXAH = (u8)((u16)CmdTmp >> 8); //SPI DMA发送命令首地址
DMA_SPI_TXAL = (u8)CmdTmp;
DMA_SPI_AMTH = 0; //设置传输总字节数(高8位), 设置传输总字节数 = N+1
DMA_SPI_AMT= 3-1; //设置传输总字节数(低8位).
DMA_SPI_CR = DMA_ENSPI | SPI_TRIG_M | SPI_TRIG_S | SPI_CLRFIFO; //启动SPI DMA发送命令
}
else
{
B_TxCmd = 0; //清除已发地址命令
P_OLED_DC = 1; //写数据
DMA_SPI_TXAH = (u8)(SPI_TxAddr >> 8); //SPI DMA发送数据首地址
DMA_SPI_TXAL = (u8)SPI_TxAddr;
DMA_SPI_AMTH = 0; //设置传输总字节数(高8位), 设置传输总字节数 = N+1
DMA_SPI_AMT= 128-1; //设置传输总字节数(低8位).
DMA_SPI_CR = DMA_ENSPI | SPI_TRIG_M | SPI_TRIG_S | SPI_CLRFIFO; //启动SPI DMA发送命令
SPI_TxAddr+= 128; //要发送数据的首地址, 一次DMA传输16字节
SPI_TxCnt++; //发送次数+1
}
}
DMA_SPI_STA = 0; //清除中断标志
}
//====================================================================================
void main(void)
{
u16 i;
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 1; //避免 DMA 与 CPU 同时访问 xdata 时产生总线冲突
P2M1 &= ~0x7c;//SPI接口设置成推挽输出
P2M0 |= 0x7c;
P2SR &= ~0x28;//P2.3(MOSI) P2.5(SCLK)设置成高速模式
HPLL_config();
SPI_Config();
Initialize_OLED();
EA = 1;
while(1)
{
for(i=0; i<1024; i++) DisTmp= 0; //清除显存
SPI_DMA_TRIG(DisTmp);
while(B_SPI_DMA_busy); //等待SPI DMA完成
printf_ASCII_text(0, 0, "OLED12864 SSD1306");
for(i=0; i<8; i++) WriteHZ16((u8)(i*16),2,i);
printf_ascii_10x24(0,5,"-12.345 678");
LCD_delay_ms(3000);
for(i=0; i<1024; i++) DisTmp = gImage_picture1; //将图片装载到显存
SPI_DMA_TRIG(DisTmp);
LCD_delay_ms(3000);
for(i=0; i<1024; i++) DisTmp = gImage_picture2; //将图片装载到显存
SPI_DMA_TRIG(DisTmp);
LCD_delay_ms(3000);
}
}
//========================================================================
// 函数: void HPLL_config(void)
// 描述: PLL时钟配置函数。
// 参数: none.
// 返回: none.
// 版本: VER1.0
// 日期: 2025-11-01
// 备注:
//========================================================================
void HPLL_config(void)
{
#if(HPLL_SEL==SEL_HPLL2)
WTST = 2; //通过WTST增加等待时钟控制Flash读取速度在33MHz以内, 主时钟:360MHz/2/2=90MHz, 读Flash速度:90MHz/(1+2)=30MHz
HPLLCR |= 0x80; //使能HPLL(高速外设默认使用HPLL时钟,切换到HPLL2前要先使能HPLL,切换完再关闭HPLL)
//首先需要将HIRC主频调节到48MHz
HPLL2CR &= ~0x10; //选择HPLL2输入时钟源为HIRC
// HPLL2CR |= 0x10; //选择HPLL2输入时钟源为IRCM
HPLL2PDIV = 8; //设置HPLL2输入时钟预分频为8(HPLL输入频率必须为6MHz)
// HPLL2CR |= 0x00; //HPLL2=6MHz*52=312MHz
// HPLL2CR |= 0x01; //HPLL2=6MHz*54=324MHz
// HPLL2CR |= 0x02; //HPLL2=6MHz*56=336MHz
// HPLL2CR |= 0x03; //HPLL2=6MHz*58=348MHz
HPLL2CR |= 0x04; //HPLL2=6MHz*60=360MHz
// HPLL2CR |= 0x05; //HPLL2=6MHz*62=372MHz
// HPLL2CR |= 0x06; //HPLL2=6MHz*64=384MHz
// HPLL2CR |= 0x07; //HPLL2=6MHz*66=396MHz
// HPLL2CR |= 0x08; //HPLL2=6MHz*68=408MHz
// HPLL2CR |= 0x09; //HPLL2=6MHz*70=420MHz
// HPLL2CR |= 0x0a; //HPLL2=6MHz*72=432MHz
// HPLL2CR |= 0x0b; //HPLL2=6MHz*74=444MHz
// HPLL2CR |= 0x0c; //HPLL2=6MHz*76=456MHz
// HPLL2CR |= 0x0d; //HPLL2=6MHz*78=468MHz
// HPLL2CR |= 0x0e; //HPLL2=6MHz*80=480MHz
// HPLL2CR |= 0x0f; //HPLL2=6MHz*82=492MHz
HPLL2CR |= 0x20; //高速外设时钟选择 HPLL2/2
HPLL2CR |= 0x80; //使能HPLL2
HPLLCR &= ~0x80; //关闭HPLL(高速外设默认使用HPLL时钟,切换到HPLL2前要先使能HPLL,切换完再关闭HPLL)
CLKDIV = 2; //系统时钟 = 主时钟源/2 = HPLL2/2/2 = 90MHz
CLKSEL = 0x08; //选择HPLL2/2作为主时钟源
#else
WTST = 3; //通过WTST增加等待时钟控制Flash读取速度在33MHz以内, 主时钟:480MHz/2/2=120MHz, 读Flash速度:120MHz/(1+3)=30MHz
//首先需要将HIRC主频调节到48MHz
HPLLCR &= ~0x10; //选择HPLL输入时钟源为HIRC
// HPLLCR |= 0x10; //选择HPLL输入时钟源为IRCM
HPLLPDIV = 8; //设置HPLL输入时钟预分频为8(HPLL输入频率必须为6MHz)
// HPLLCR |= 0x00; //HPLL=6MHz*52=312MHz
// HPLLCR |= 0x01; //HPLL=6MHz*54=324MHz
// HPLLCR |= 0x02; //HPLL=6MHz*56=336MHz
// HPLLCR |= 0x03; //HPLL=6MHz*58=348MHz
// HPLLCR |= 0x04; //HPLL=6MHz*60=360MHz
// HPLLCR |= 0x05; //HPLL=6MHz*62=372MHz
// HPLLCR |= 0x06; //HPLL=6MHz*64=384MHz
// HPLLCR |= 0x07; //HPLL=6MHz*66=396MHz
// HPLLCR |= 0x08; //HPLL=6MHz*68=408MHz
// HPLLCR |= 0x09; //HPLL=6MHz*70=420MHz
// HPLLCR |= 0x0a; //HPLL=6MHz*72=432MHz
// HPLLCR |= 0x0b; //HPLL=6MHz*74=444MHz
// HPLLCR |= 0x0c; //HPLL=6MHz*76=456MHz
// HPLLCR |= 0x0d; //HPLL=6MHz*78=468MHz
HPLLCR |= 0x0e; //HPLL=6MHz*80=480MHz
// HPLLCR |= 0x0f; //HPLL=6MHz*82=492MHz
HPLL2CR &= ~0x60; //高速外设时钟选择 HPLL/2
HPLLCR |= 0x80; //使能HPLL
CLKDIV = 2; //系统时钟=主时钟源/2 = HPLL/2/2 = 120MHz
CLKSEL = 0x04; //选择HPLL/2作为主时钟源
#endif
// P5M0 |= 0x10; P5M1 &= ~0x10;//设置P5.4口为推挽输出
// P5SR &= ~0x10; //设置P5.4口为快速模式
// HIRCCR |= 0x10; //输出系统时钟
// MCLKOCR = 10; //系统时钟 10 分频到P5.4
}
页:
[1]