Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LCD
SPI
M2354
【NuMaker-M2354试用】+SPI驱动LCD及TOUCH
发布于 2021-12-01 15:44:00 浏览:1196
订阅该版
今天抽空驱动了收拾的一款兼容UNO接口的SPI屏幕~LCD分辨率240X320,驱动芯片HX8347D,触摸芯片XPT2046 ,共用一个SPI(屏幕上还挂了一个SD,这里不用,因为M2354板子上有SDIO接口的SD卡座)~ 我们先看官方对SPI模块的框架描述: ![Z1.png](https://oss-club.rt-thread.org/uploads/20211214/93c4b2dd5a1d6aca99c063290f0f7175.png) 我们这了用的是SPI0,我们看下我们复用的引脚: ![Z2.png](https://oss-club.rt-thread.org/uploads/20211214/3dc935eb9d208c4d70667e2870bd824c.png) 我们查看下板子硬件: LCD屏幕引脚分布如图: ![Z1.png](https://oss-club.rt-thread.org/uploads/20211201/452972e75183ec7901a6ed4dd93df2f5.png) M2345对应UNO座子引脚: ![Z2.png](https://oss-club.rt-thread.org/uploads/20211201/60a13456d43c79eb5be4fd949d133154.png) 我们得知: LCD_MOSI-->PA0 LCD_MISO-->PA1 LCD_SCK -->PA2 LCD_CS -->PA3 LCD_BKL -->PC10 LCD_DC -->PC11 SD_CS -->PE6 TP_CS -->PE7 TP_IRQ -->PC0 硬件对于好后,我们开始写程序: 由于程序就要RTT,我们先通过env工具,对官方的DEMO例程,通过menuconfig进行精简,几乎去掉所有上层,着重在RTT COMPONENT及新唐本身的CHIP级驱动。之前仅用过RTT的NANO版本,仅用了框架,对其组件如何使用还不是很熟悉。我们就从最基本的开始。 ![Z3.png](https://oss-club.rt-thread.org/uploads/20211214/19ea43e0c68d6f60ac1856489df1475a.png) 我们开启RTT COMPONENT的SPI及NU CHIP的SPI: ![Z5.png](https://oss-club.rt-thread.org/uploads/20211214/4405676cc10e5c6ea4f7e8b085e6d0b8.png) ![Z6.png](https://oss-club.rt-thread.org/uploads/20211214/71b0c831a9431ad35593bbd5fb0e9128.png) ![Z7.png](https://oss-club.rt-thread.org/uploads/20211214/a88a623731e7728f17363c00d181e2f0.png) ![Z8.png](https://oss-club.rt-thread.org/uploads/20211214/a94506f70d8498137432328fc0e34b0e.png) 其应用层的均无勾选,我们保存修改后,通过scons指令生成并编译工程。 下面我们对该工程进行修改添加自己的代码。 其中PA0~PA3为SPI0的四个驱动引脚,CS引脚我们不服用,我们手动控制: SPI0初始化: ```c void SPI0_Init(u32 spi_baud) { /* Enable SPI0 peripheral clock */ CLK_EnableModuleClock(SPI0_MODULE); /* Select SPI0 peripheral clock source as PCLK1 */ CLK_SetModuleClock(SPI0_MODULE, CLK_CLKSEL2_SPI0SEL_PCLK1, MODULE_NoMsk); /* Setup SPI0 multi-function pins */ SYS->GPA_MFPL &= ~(SYS_GPA_MFPL_PA0MFP_Msk | SYS_GPA_MFPL_PA1MFP_Msk | SYS_GPA_MFPL_PA2MFP_Msk /*| SYS_GPA_MFPL_PA3MFP_Msk*/); SYS->GPA_MFPL |= (SYS_GPA_MFPL_PA0MFP_SPI0_MOSI | SYS_GPA_MFPL_PA1MFP_SPI0_MISO | SYS_GPA_MFPL_PA2MFP_SPI0_CLK /*| SYS_GPA_MFPL_PA3MFP_SPI0_SS*/); /* Set LCD PA3片选引脚为输出模式 */ GPIO_SetMode(PA,BIT13,GPIO_MODE_OUTPUT); /* Configure SPI0 as a master, MSB first, 8-bit transaction, SPI Mode-0 timing, clock is 2MHz */ SPI_Open(SPI0, SPI_MASTER, SPI_MODE_0, 8, spi_baud);//2000000 2M /* Disable auto SS function, control SS signal manually. */ SPI_DisableAutoSS(SPI0); } ``` 这里面,其实在生成工具的nutool_pincfg里面有外设引脚默认的连接配置: 我们修改: ![M0.png](https://oss-club.rt-thread.org/uploads/20211214/1b5b340af10c9aa2854764fe3b37fe97.png) 我们这了也仅有了RTT的框架,对其包装过的SPI接口函数未使用: 我们再写一个SPI读写一个字节的函数: ```c u8 SPI0_ReadWriteByte(u8 tx_data) { u8 recdata=0; SPI_WRITE_TX(SPI0, tx_data); while(SPI_IS_BUSY(SPI0)); recdata = (uint8_t)SPI_READ_RX(SPI0); return recdata; } ``` 下面我们写LCD及XPT2046相关驱动,具体查看工程文件: 其中对触摸校准的数值,我们保存到了FMC的APROM BANK1的最后2KB的位置~ 目前使用16个字节,即4字~ ```c void FMC_APROM_OPEN(void) { /* Enable FMC ISP function */ FMC_Open(); /* Enable FMC erase/program APROM */ FMC_ENABLE_AP_UPDATE(); } void FMC_APROM_CLOSE(void) { /* 取消APROM执行更新操作 */ FMC_ENABLE_AP_UPDATE(); /* 取消FMC */ FMC_Close(); } //保存LCD校验参数 //占用14个字节 15个字节为是否校验标志 16字节为0 void FMC_APROM_WRITEDATAS(u8* buf) { PROTECT_REG ( FMC_APROM_OPEN(); u8 i=0; u32 tmp=0; /* FMC对APROM某一起始地址进行擦除操作,擦除大小为2K字节*/ FMC_Erase(APROM_SAVE_ADDRESS); for(i=0;i<4;i++) { tmp=(buf[i*4]<<24)+(buf[i*4+1]<<16)+(buf[i*4+2]<<8)+buf[i*4+3]; /* 向APROM写入数据,每次写入1个字即4个字节 */ //FMC_Write(APROM_SAVE_ADDRESS+i*4,&buf[i*4]); FMC_Write(APROM_SAVE_ADDRESS+i*4,tmp); } FMC_APROM_CLOSE(); ) } void FMC_APROM_WRITEWORDS(u32* buf) { PROTECT_REG ( FMC_APROM_OPEN(); u8 i=0; u32 tmp=0; /* FMC对APROM某一起始地址进行擦除操作,擦除大小为2K字节*/ FMC_Erase(APROM_SAVE_ADDRESS); for(i=0;i<4;i++) { tmp=buf[i]; /* 向APROM写入数据,每次写入1个字即4个字节 */ //FMC_Write(APROM_SAVE_ADDRESS+i*4,&buf[i*4]); FMC_Write(APROM_SAVE_ADDRESS+i*4,tmp); } FMC_APROM_CLOSE(); ) } void FMC_APROM_READDATAS(u8* buf) { PROTECT_REG ( FMC_APROM_OPEN(); u8 i=0; u32 tmp=0; for(i=0;i<4;i++) { /* 从APROM读入数据,每次读入1个字即4个字节 */ //buf[i]=FMC_Read(APROM_SAVE_ADDRESS+i*4); tmp=FMC_Read(APROM_SAVE_ADDRESS+i*4); buf[i*4] =tmp>>24; buf[i*4+1]=tmp>>16; buf[i*4+2]=tmp>>8; buf[i*4+3]=tmp; } FMC_APROM_CLOSE(); ) } void FMC_APROM_READWORDS(u32* buf) { PROTECT_REG ( FMC_APROM_OPEN(); u8 i=0; u32 tmp=0; for(i=0;i<4;i++) { /* 从APROM读入数据,每次读入1个字即4个字节 */ //buf[i]=FMC_Read(APROM_SAVE_ADDRESS+i*4); tmp=FMC_Read(APROM_SAVE_ADDRESS+i*4); buf[i] =tmp; } FMC_APROM_CLOSE(); ) } ``` 对于浮点数的保存,我们使用了联合体的方法,将其拆成U8数组~ 联合体看似与结构体相似,其不同之处在于结构体中每个变量占用不同的内存,而联合体共用一段内存,这就给保存浮点数提供了极大的便利。: 比如我要保存一个浮点数a,我定义一个联合体 union { float x; uchar s[4]; }F32_Sep; 然后把a的值赋给F32_Sep.x,这样一来其实s[0]对应了浮点数的最高位,s[3]对应其最低位,因此,保存到EEPROM中只需将s[0]~s[3]即可。 这种方法甚至可以保存符号~ 读取时,先把读取的字节依次保存到S[0]~S[3].然后直接应用把F32_Sep.x赋给要操作的浮点变量即可~ 本程序里面的应用: ```c union { float x; unsigned char s[4]; }F32_Sep_fXfac; union { float x; unsigned char s[4]; }F32_Sep_fYfac; //保存校准参数 void tp_save_ajdata(void) { u32 tmpbuf[4]={0}; //强制保存&tp_dev.xfac地址开始的12个字节数据,即保存到tp_dev.touchtype //在最后,写0X0A标记校准过了 F32_Sep_fXfac.x=s_tTouch.fXfac; F32_Sep_fYfac.x=s_tTouch.fYfac; tmpbuf[0]=(F32_Sep_fXfac.s[0]<<24)+(F32_Sep_fXfac.s[1]<<16)+(F32_Sep_fXfac.s[2]<<8)+F32_Sep_fXfac.s[3]; tmpbuf[1]=(F32_Sep_fYfac.s[0]<<24)+(F32_Sep_fYfac.s[1]<<16)+(F32_Sep_fYfac.s[2]<<8)+F32_Sep_fYfac.s[3]; tmpbuf[2]=(s_tTouch.iXoff<<16)+s_tTouch.iYoff; tmpbuf[3]=0x0A; FMC_APROM_WRITEWORDS(tmpbuf); } //得到保存在EEPROM里面的校准值 //返回值:1,成功获取数据 // 0,获取失败,要重新校准 u8 tp_get_adjdata(void) { u32 rxbuf[4]={0}; //读取之前保存的校准数据 FMC_APROM_READWORDS(rxbuf);//读取标记字,看是否校准过! if(rxbuf[3]==0X0A)//触摸屏已经校准过了 { //赋值联合体数值 F32_Sep_fXfac.s[0]=rxbuf[0]>>24; F32_Sep_fXfac.s[1]=rxbuf[0]>>16; F32_Sep_fXfac.s[2]=rxbuf[0]>>8; F32_Sep_fXfac.s[3]=rxbuf[0]; s_tTouch.fXfac=F32_Sep_fXfac.x; F32_Sep_fYfac.s[0]=rxbuf[1]>>24; F32_Sep_fYfac.s[1]=rxbuf[1]>>16; F32_Sep_fYfac.s[2]=rxbuf[1]>>8; F32_Sep_fYfac.s[3]=rxbuf[1]; s_tTouch.fYfac=F32_Sep_fYfac.x; s_tTouch.iXoff=(u16)(rxbuf[2]>>16); s_tTouch.iYoff=(u16)rxbuf[2]; return 1; } return 0; } ``` 我们,添加3个任务一个是LED闪烁,一个是串口定时输出,一个就是点击屏幕显示坐标: 在点击屏幕显示坐标任务里面,我们先检查触摸有无校准,如果有校准标志,直接跳过~无校准标志,强行校准~while循环里面:我们查询触摸,一旦触摸显示触摸的坐标~ ``` void tp_Mytouched(void) { tp_scan(0); if (s_tTouch.chStatus & TP_PRESS_DOWN) { if (s_tTouch.hwXpos < LCD_WIDTH && s_tTouch.hwYpos < LCD_HEIGHT) { sprintf((char*)str, "the posion is x: %d,Y: %d",s_tTouch.hwXpos, s_tTouch.hwYpos); lcd_clear_screen(WHITE); lcd_display_string(10, 80, str, 16, RED); LEDY_TOG(); } } } ``` 我们把一些初始化放到RTT的DRV_COMMON的板级初始化函数里面: ``` RT_WEAK void rt_hw_board_init(void) { /* Init System/modules clock */ //nutool_modclkcfg_init(); /* Unlock protected registers */ SYS_UnlockReg(); /* Init all pin function setting */ nutool_pincfg_init(); SYS_Init(PLL_CLOCK); systick_rtos_delayInit(); /* 串口0初始化,波特率为115200bps */ UART0_Init(115200); SPI0_Init(8000000); #if defined(BSP_USING_EADC) /* Vref connect to internal */ SYS->VREFCTL = (SYS->VREFCTL & ~SYS_VREFCTL_VREFCTL_Msk) | SYS_VREFCTL_VREF_3_0V; #endif //rt_hw_lcd_init(); /* Lock protected registers */ SYS_LockReg(); #ifdef RT_USING_HEAP rt_system_heap_init(HEAP_BEGIN, HEAP_END); #endif /* RT_USING_HEAP */ #if defined(BSP_USING_UART) rt_hw_uart_init(); #endif #ifdef RT_USING_CONSOLE rt_console_set_device(RT_CONSOLE_DEVICE_NAME); #endif #ifdef RT_USING_COMPONENTS_INIT rt_components_board_init(); #endif } ``` 对于systick的初始化我们单独写了个函数,其实与CM23内核自带的类似: ``` void systick_rtos_delayInit(void) //在RTT BOARD里面被调用初始化 { fac_ms=1000/RT_TICK_PER_SECOND; /*UCOS每秒钟的计数次数*/ SysTick->LOAD = fac_ms * CyclesPerUs *1000; /*为NVIC SysTick中断设置优先级*/ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /*清零计数器*/ SysTick->VAL = 0; /*使用外部12MHz时钟,并使能SysTick中断*/ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; } ``` 我们用的是外部晶振,屏蔽工程自动生成的CLOCK相关的函数,重新编写: 自动生成的在nutool_modclkcfg.c里面,我们屏蔽掉即可: ![M3.png](https://oss-club.rt-thread.org/uploads/20211214/029e714c73cf5ba6367eaee849a6712c.png) 我们定义自己的时钟初始化函数: ``` void SYS_Init(uint32_t u32PllClock) { /* 使能外部晶振时钟(12MHz) */ CLK_EnableXtalRC(CLK_PWRCTL_HXTEN_Msk); /* 等待外部晶振时钟(12MHz) */ CLK_WaitClockReady(CLK_STATUS_HXTSTB_Msk); /* 选择内部PLL作为HCLK,同时HCLK的分频值为2 */ CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_PLL, CLK_CLKDIV0_HCLK(2)); /* 设置内核时钟为PLL_CLOCK */ CLK_SetCoreClock(u32PllClock); /* 设置SysTick的时钟源为外部晶振 */ CLK_SetSysTickClockSrc(CLK_CLKSEL0_STCLKSEL_HXT); } ``` 我们对每个外设独立新建文件初始化,便于移植: ![M4.png](https://oss-club.rt-thread.org/uploads/20211214/e9b0620bff3e0213650387c8fafb8acc.png) 具体可查看代码。 创建3个任务并运行: ``` /**************************************************************************//** * * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-8-26 Wayne First version * ******************************************************************************/ #include
#include
#include
#include "common.h" #include "led.h" #include "key.h" #include "spi0.h" #include "tftlcd.h" #include "fonts.h" #include "xpt2046.h" #include "touch.h" #include "fmc_savedata.h" /* defined the LEDR pin: PD3 */ #define LEDR NU_GET_PININDEX(NU_PD, 3) /* defined the LEDY pin: PD2 */ #define LEDY NU_GET_PININDEX(NU_PD, 2) uint8_t str[50]; void tp_Mytouched(void) { tp_scan(0); if (s_tTouch.chStatus & TP_PRESS_DOWN) { if (s_tTouch.hwXpos < LCD_WIDTH && s_tTouch.hwYpos < LCD_HEIGHT) { sprintf((char*)str, "the posion is x: %d,Y: %d",s_tTouch.hwXpos, s_tTouch.hwYpos); lcd_clear_screen(WHITE); lcd_display_string(10, 80, str, 16, RED); LEDY_TOG(); } } } void led_thread_entry(void* parameter) { /* set LEDR pin mode to output */ rt_pin_mode(LEDR, PIN_MODE_OUTPUT); while (1) { rt_pin_write(LEDR, PIN_HIGH); rt_thread_mdelay(500); rt_pin_write(LEDR, PIN_LOW); rt_thread_mdelay(500); } } void led2_thread_entry(void* parameter) { /* set LEDR pin mode to output */ // rt_pin_mode(LEDY, PIN_MODE_OUTPUT); while (1) { printf("Hello world!\r\n"); rt_thread_mdelay(800); } } void touch_thread_entry(void* parameter) { while(1) { tp_Mytouched(); rt_thread_mdelay(20); } } int main(int argc, char **argv) { rt_thread_t led_tast;// 线程句柄 rt_thread_t led2_tast;// 线程句柄 rt_thread_t touch_tast; //PROTECT_REG //( //SYS_Init(PLL_CLOCK); //UART0_Init(115200); //SPI0_Init(8000000); //) LED_Init(); KEY_Init(); lcd_port_init(); lcd_init(); xpt2046_port_init(); tp_init(); printf("%f,%f,%d,%d\r\n",s_tTouch.fXfac,s_tTouch.fYfac,s_tTouch.iXoff,s_tTouch.iYoff); /* 创建led 线程*/ led_tast = rt_thread_create("led_tast_name", led_thread_entry , RT_NULL, 1024, 4, 10); /* 创建成功则启动线程*/ if (led_tast != RT_NULL) rt_thread_startup(led_tast); /* 创建led 线程*/ led2_tast = rt_thread_create("led2_tast_name", led2_thread_entry , RT_NULL, 1024, 4, 10); /* 创建成功则启动线程*/ if (led2_tast != RT_NULL) rt_thread_startup(led2_tast); /* 创建led 线程*/ touch_tast = rt_thread_create("touch_tast_name", touch_thread_entry , RT_NULL, 1024, 4, 10); /* 创建成功则启动线程*/ if (touch_tast != RT_NULL) rt_thread_startup(touch_tast); return 0; } ``` 编译下载后,屏幕反应正常: ![Z4.jpg](https://oss-club.rt-thread.org/uploads/20211201/9ce2802b24b2c53b577c1ecaf5b7b4b7.jpg.webp) LED定时闪烁: ![CC.gif](https://oss-club.rt-thread.org/uploads/20211214/bac36fe2aabafd5f4010b5c58a1abc21.gif) 串口定时输出: ![S2.png](https://oss-club.rt-thread.org/uploads/20211214/34e82d07ab4b4b328fc2655fe1dcffe0.png) 总之,新唐的MCU没得说,很不错,外设齐全,主频和内存都不吝啬,给的例子上手容易,符合国人的思维习惯~RTT呢,也算是狠狠摸索了一把,之前禁言用过RTT NANO,看了标准版的RTT有如此多的组件,还是比较眼馋的,奈何自己还不是很会。只好有时间慢慢摸索了~当然,这是一次很棒的体验~谢谢新唐和RTT给我这次机会。 之前的裸机工程:[m2345_demoCode.rar](https://oss-club.rt-thread.org/uploads/20211201/3d269169d4a7db62fd065f66f0a0844d.rar) 带RTT的工程:[numaker-m2354_Thin_BAK.rar](https://oss-club.rt-thread.org/uploads/20211214/9307508e867c0ba411f0883dfb0a8a41.rar)
2
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
qjp1988113
这家伙很懒,什么也没写!
文章
3
回答
0
被采纳
0
关注TA
发私信
相关文章
1
BBB的SPI驱动
2
求个SPI上挂两个或多个设备的使用例子
3
SPI设备有个bug
4
spi flash 的fatfs使用一段时间后读写文件出现故障
5
SPI驱动
6
请教rt_spi_configure函数理解
7
SPI FLASH挂载的问题
8
SPI-FLASH 文件系统 SPIFFS
9
求助一个完整的 spi flash 驱动
10
关于同时使用文件系统与SPI FLASH的问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
xiaorui
1
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部