上一篇我们移植了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区域中填充颜色(也就是绘制一个矩形)
同样的,根据进度在绘制一个小的矩形就可以了,是不是很简单
好了,其他界面我们就不介绍了,我把程序放到了附件,感兴趣的可以下载看看。
下一篇:实现蓝牙传书
我要赚赏金
