上一篇我们移植了arm公司开源的arm-2d,这一篇就讲一下怎么使用arm-2d的api来制作电子书的界面。
界面效果如下图
这次一共制作4个界面(图标选择界面、电子书目录、电子书阅读界面和单词卡界面)
硬件接线
好,首先我们先看一下这次的硬件连接。这次需要一张micro SD卡来保存txt文件和屏幕显示需要的字库文件,
所以硬件链接如下图
其中SD卡是接到板子的spi1接口,共4根线,如下
PB4 ------> SPI1_SCK
PB3 ------> SPI1_MISO
PA15 ------> SPI1_MOSI
PA12 ------> SPI1_CS
屏幕还是gpio模拟iic,第一篇文章已经介绍过了,这里就不在介绍了。
驱动SD卡
接线已经讲完了,就来看看SD卡的驱动程序。首先用stm32cubeMX生成spi1的驱动代码,
由于不太会用这个软件,所以生成后的代码还需要简单修改一下,修改好的代码如下
SPI_HandleTypeDef hspi1; static void MX_SPI1_Init(void) { SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0}; /* USER CODE BEGIN SPI1_Init 1 */ /* USER CODE END SPI1_Init 1 */ /* SPI1 parameter configuration*/ hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES;//工作模式为全双工模式 hspi1.Init.DataSize = SPI_DATASIZE_8BIT;//设置SPI的数据大小:SPI发送接收8位帧结构 hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;//选择了串行时钟的稳态:时钟悬空高 hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;//数据捕获于第二个时钟沿 hspi1.Init.NSS = SPI_NSS_SOFT;//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制 hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//定义波特率预分频的值:波特率预分频值为256 hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;//指定数据传输从MSB位2还是LSB位开始:数据传输从MSB位开始 hspi1.Init.TIMode = SPI_TIMODE_DISABLE;//具体来说是禁用TI模式 hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 0x7;//CRC值计算的多项式 hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;//启用NSS脉冲模式 hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_HIGH;//具体来说是将NSS信号的空闲状态设置为高电平 hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;//具体来说是将FIFO阈值设置为1个数据。 hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;//具体来说是将片选空闲周期设置为0个时钟周期。 hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;//具体来说是将帧间空闲周期设置为0个时钟周期。 hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;//STM32微控制器中SPI接口的自动接收暂停功能(Master Receiver Auto-Suspend),具体来说是禁用该功能。 hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;//这行代码用于配置STM32微控制器中SPI接口的I/O状态保持功能(Master Keep IO State),具体来说是禁用该功能 hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;//这行代码用于配置STM32微控制器中SPI接口的I/O引脚交换功能(IO Swap),具体来说是禁用该功能。 hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;//具体来说是将就绪信号的管理设置为内部管理(Internally)。 hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;//具体来说是将就绪信号的空闲状态设置为高电平。 if (HAL_SPI_Init(&hspi1) != HAL_OK) { //Error_Handler(); } HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;//具体来说是将自主模式触发状态设置为禁用。 if (HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct) != HAL_OK) { //Error_Handler(); } /* USER CODE BEGIN SPI1_Init 2 */ /* USER CODE END SPI1_Init 2 */ }
其他sd卡的程序我们就不多讲了,到时候会上传到附件。
简单测试一下SD卡,看看是否连接正确,测试程序如下
SD_HW_Config(); int result = SD_Init(); if(result){ draw_string_fast_EN(0,16,12,"01"); }else{ draw_string_fast_EN(0,16,12,"ok"); }
如果屏幕中显示OK,则sd卡连接正确
挂载fatfs文件系统
sd卡驱动没问题后,接下来就是挂载fatfs文件系统,具体移植就不做介绍了,简单的测试程序如下
#define MUSIC_PATH_NEWFILE_TXT "/大学.txt" void test_read_file_fatfs(){ FIL file; // 文件对象 FRESULT res; // FatFs返回结果 char out_buff[128]; uint32_t rd_len; //res = f_open(&file, MUSIC_PATH_NEWFILE_TXT, FA_READ);//FA_CREATE_ALWAYS | FA_WRITE res = f_open(&file, MUSIC_PATH_NEWFILE_TXT, FA_CREATE_ALWAYS | FA_WRITE);//FA_CREATE_ALWAYS | FA_WRITE if (res == FR_OK) { for(int i = 0; i < 4;i++){ res = f_lseek(&file, (2+i)*128); if(res == FR_OK){ res = f_read(&file, out_buff, 128, &rd_len); draw_string_fast_EN(16,16*i ,16,out_buff); } } draw_string_fast_EN(0,16,12," fatfs ok"); } f_close(&file) ; } int main(void) { ...// f_mount(&fatfs,"",1);//挂载文件系统 test_read_file_fatfs(); ... }
如果屏幕中显示fatfs ok,则文件系统挂载成功。
好了,接下来,就进入今天的主题,编写一个电子书的界面。
scene简介
在arm-2d中,每一个scene就相当于一个界面,所以我们需要几个界面就在rte中生成几个scene就可以了,如下图
直接输入4,点击确定就会出现4个scene,如下图
我们做的电子书需要4个界面,其效果如上面的gif所示。
图标界面
下面就开始制作第一个图标界面,如下图
此界面也很简单,一共有3个图标(分别是 电子书、单词卡和音乐),
按下板子上的按键1,3个图标会左右切换,并且在中间的图标处于选中状态,他的图标是反色绘制的。
中间图标的下面也会显示对应的汉字。
好了,这个界面的需求知道了,我们就开始制作吧。
图标界面布局
首先我们用arm-2d的布局小工具dock对此界面进行布局。
布局小工具dock简介
你可能还不知道什么是dock小工具,那我们就先简单介绍一下。
比如,你要获取屏幕上方高度为30的区域,如下图所示
此时,就可这样写
arm_2d_canvas(ptTile, _canvas) { arm_2d_dock_top( _canvas, 30 ) { __top_region } }
arm_2d_canvas是用来获取屏幕大小的
arm_2d_dock_top就是用来获取屏幕上方的区域,此时生成的区域名称就叫__top_region(也就是说它的名字是固定的)
同样的,Arm-2D还提供了获取屏幕左边、右边和下方区域的小工具,如下表所示
工具名称 | 对应区域名称 |
arm_2d_dock_bottom(__region, __height) | __bottom_region |
arm_2d_dock_left(__region, __width) | __left_region |
arm_2d_dock_right(__region, __width) | __right_region |
那我想获取屏幕中间的区域呢,比如高度为30,宽度为屏幕宽度的区域(如下图所示)该怎么办呢?
哈哈,这个也简单,贴心的官方也给我们提供了工具,如下
arm_2d_canvas(ptTile, _canvas) { arm_2d_dock_vertical( _canvas, 30 ) { __vertical_region } }
__vertical_region就是我们想要的区域了
同样的,获取屏幕中间竖条区域的小工具也有,如下
arm_2d_dock_horizontal(__region, __width)
它对应的区域名称为__horizontal_region
当然了,除了dock小工具,arm-2d还有其他的布局小工具,比如在区域中央可以用arm_2d_align_centre
arm_2d_canvas(ptTile, _canvas) { arm_2d_align_centre(_canvas, 100,100 ) { __centre_region } }
好了,布局小工具就先介绍到这里。
其他布局工具可以看这篇文章: https://mp.weixin.qq.com/s/m8Cvi--nl9I700kpskqqmg
接着就该今天的图标界面布局了。
首先,
此界面分为上下两个部分,如下图
对应的代码就可以这样写
arm_2d_dock_top( my_region, 50 ) { } arm_2d_dock_bottom(my_region, 20){ }
而上面的图标区域又分为3个,如下图
此时代码就可以这样写
arm_2d_dock_top( my_region, 50 ) { //__top_region arm_2d_dock_vertical( __top_region, 40 ) { arm_2d_align_centre(__vertical_region, 40,40 ) { //中间的区域 } arm_2d_dock_left(__vertical_region, 40){ //左边的区域 } arm_2d_dock_right(__vertical_region, 40){ //右边的区域 } } }
下边区域的汉字也是在屏幕中间,所以代码就可以这样写
arm_2d_dock_bottom(my_region, 20){ arm_2d_align_centre(__bottom_region, 16*3,16 ) { } }
到此,图标界面的布局就完成了。
接着在这几个区域中绘制图标就可以了。
图标的绘制
要绘制图标,首先讲几个绘制函数。
由于我们使用的是单色屏幕,所以可以直接用A1的素材来绘制,其函数如下
arm_2d_fill_colour_with_a1_mask(ptTile, &my_region, &c_tileCar24A1Mask, (__arm_2d_color_t){GLCD_COLOR_WHITE});
其中,my_region就是我们要在屏幕的哪个区域绘制
c_tileCar24A1Mask就是我们的图片素材
GLCD_COLOR_WHITE就是素材填充的颜色
由于我们使用的是arm-2d的8bit模式驱动oled的,所以也可以直接使用A8素材来填充,其函数如下
arm_2d_fill_colour_with_a8_mask(ptTile, &my_region, &c_tileCar24A8Mask, (__arm_2d_color_t){GLCD_COLOR_WHITE});
那你这个A1mask和A8 mask是怎么制作的呢?
其实他就是一个取模软件,不过arm-2d也提供了一个python工具,如下
他的使用如下
这样,就可以生成40*40的图标素材了。
绘制界面
好,接下来就把图标界面绘制一下,程序如下
void draw_icon(void *pTarget, const arm_2d_tile_t *ptTile, arm_2d_region_t my_region, bool bIsNewFrame){ arm_2d_dock_top( my_region, 50 ) { //__top_region arm_2d_dock_vertical( __top_region, 40 ) { //__vertical_region arm_2d_align_centre(__vertical_region, 40,40 ) { arm_2d_fill_colour_with_a8_mask(ptTile, &__centre_region, my_icon[my_icon_list.current_entry], (__arm_2d_color_t){GLCD_COLOR_WHITE}); __centre_region.tLocation.iX -= 1; __centre_region.tLocation.iY -= 1; __centre_region.tSize.iHeight += 2; __centre_region.tSize.iWidth += 2; arm_2d_filter_reverse_colour(ptTile, &__centre_region); } arm_2d_dock_left(__vertical_region, 40){ char num; __left_region.tLocation.iX -= 10; if(0 == my_icon_list.current_entry){ num = my_icon_list.total_entries_num; }else{ num = my_icon_list.current_entry; } arm_2d_fill_colour_with_a8_mask(ptTile, &__left_region, my_icon[num - 1], (__arm_2d_color_t){GLCD_COLOR_WHITE}); } arm_2d_dock_right(__vertical_region, 40){ __right_region.tLocation.iX += 10; arm_2d_fill_colour_with_a8_mask(ptTile, &__right_region, my_icon[(my_icon_list.current_entry + 1) % my_icon_list.total_entries_num ], (__arm_2d_color_t){GLCD_COLOR_WHITE}); } } } arm_2d_dock_bottom(my_region, 20){ arm_2d_align_centre(__bottom_region, 16*3,16 ) { draw_string_2d(__centre_region.tLocation.iX, __centre_region.tLocation.iY, 16,my_icon_string[my_icon_list.current_entry], pTarget, ptTile); } } }
我们还使用了一个颜色反转的函数
arm_2d_filter_reverse_colour(ptTile,&__centre_region);
这个函数就是把__centre_region区域的数据进行反色显示,这样就相当于我们选中了图标
好了,这个界面就先讲到这里
电子书阅读界面
接下来在制作一个电子书阅读界面,如下图
这个界面布局也很简单,也就左右两个部分,所以他的布局如下
arm_2d_dock_left(my_region, 120){ } arm_2d_dock_right(my_region, 11){ }
左边用来显示4行文字,也很简单,如下
arm_2d_dock_left(my_region, 120){ write_line( __left_region,out_buff,16); draw_string_2d(0, 16 * 0, 16,&my_book.lines[0][0], pTarget, ptTile); draw_string_2d(0, 16 * 1, 16,&my_book.lines[1][0], pTarget, ptTile); draw_string_2d(0, 16 * 2, 16,&my_book.lines[2][0], pTarget, ptTile); draw_string_2d(0, 16 * 3, 16,&my_book.lines[3][0], pTarget, ptTile); }
右边绘制了一个进度条,程序如下
arm_2d_dock_right(my_region, 11){ arm_2d_dock_horizontal(__right_region, 2){ arm_2d_fill_colour(ptTile, &__horizontal_region, GLCD_COLOR_WHITE); __horizontal_region.tLocation.iX -= 2; __horizontal_region.tLocation.iY = 128 * my_book.current_lseek / my_book.book_size; if(__horizontal_region.tLocation.iY > 125){ __horizontal_region.tLocation.iY = 125; } __horizontal_region.tSize.iWidth = 6; __horizontal_region.tSize.iHeight = 3; arm_2d_fill_colour(ptTile, &__horizontal_region, GLCD_COLOR_WHITE); } }
首先我们现在右边区域绘制一个竖线,其函数为
arm_2d_fill_colour(ptTile, &__horizontal_region, GLCD_COLOR_WHITE);
这个函数就是把__horizontal_region区域中填充颜色(也就是绘制一个矩形)
同样的,根据进度在绘制一个小的矩形就可以了,是不是很简单
好了,其他界面我们就不介绍了,我把程序放到了附件,感兴趣的可以下载看看。
下一篇:实现蓝牙传书