# 一、前言
## 1.功能介绍:
在ART-Pi主控上增加热敏打印机扩展板。在这个扩展板上集成了热敏打印机头,3个LED和3个按键。将手机连接到板子WIFI,通过手机APP可以打印汉字,(因为WIIF没有搞定,所以这个功能用串口替代了)二维码和图片等信息,LED和按键可以提示运行状态和交互。
## 2.简单描述:
**(原计划)**
将底板上的WIFI配置为服务器,手机APP为客户端,通过局域网传输数据。
打印的汉字将会提前烧录到存储芯片中,将整个字库烧写到flash中,在需要的时候去查询获取字模。
利用二维码生成算法将汉字转化为二维码并打印。
## 3. ART_PI和扩展板的展示图
**ART_PI展示图**
![image.png](//image.lceda.cn/pullimage/NgZHRhwmKFAxjJXv945SMbwX2VOSXGrpj3KcvpWE.png)
**打印机扩展板
正面**
![image.png](//image.lceda.cn/pullimage/nVu3Qhsb8Hij5eYFVdXWlZTN1E5GNxU8Nrk8ztxG.png)
**反面**
![image.png](//image.lceda.cn/pullimage/FxobcEloBQK1RHFuvHp2riFBiytJpmavC03ivY51.png)
**和ART_Pi连接图
前面**
![image.png](//image.lceda.cn/pullimage/WNl3Th1u8l6ymID6atGhJID01hX20POy2ERhmR0w.png)
**侧面**
![image.png](//image.lceda.cn/pullimage/TPxK8bmIpvEVVRdaRQzSjqNs47XLxLnYKcC3S8qv.png)
**运行demo程序**
![image.png](//image.lceda.cn/pullimage/jKVqCYkDZl65rpoVS6wrx5SKNUveEE7UEXPNEQam.png)
# 二、致歉
非常感谢立创EDA和RT_Thread认可我的项目,本人非常荣幸。但是因为我放假这段时间一直在做一个树莓派的项目,可能在时间上就有点分布不均,ART_Pi邮回来之后也一直是吃灰状态,本人感到非常抱歉。临近开学了,感觉RT_Thread活动也快结束了,既然那么认可我,我觉得应该不能让自己失望。于是乎,准备抽一点时间去完成这个项目。
# 三、ART_PI的入手
还记得第一次运行ART_PI,因为什么都不懂,在RT_Thread中就创建了一个RT_Thread的项目,烧录程序之后,终端就是不显示,不知道什么原因。因为我的扩展板把串口1引了出来,于是我在经过简单的修改之后就使用串口1做控制台了,但是这样就很不方便,有点浪费了。在经过一段时间的使用之后,偶然间在群里发现他们都是基于开发板的创建的工程,我想,原来是这个样子,我真是猪啊。然后我就创建了一个基于ART_PI的工程项目,但是下载进去之后,什么也没出现,控制台依然是不能使用,感觉程序好像就没烧写进去一样。又偶然间听到了bootloader这个名词,搜了一下,发现了原因。原来是ART_PI这个主控flash只有128K,要是工程大于128K的,都要烧写到外部的flash中,内部flash就烧写这个bootloader作为引导。在这简单的介绍之后,茅舍顿开,然后就进入到RT\_Thread studio一顿操作,重新烧写了bootloader(基于ART_PI的示例工程中有)。原来的bootloader因为被我烧写了其他的工程已经被破坏掉了。烧写了bootloader之后再烧写基于开发板的demo工程,于是乎,控制台可以用了,灯也正常闪烁,第一步终于跨出来了,真好。**提醒一下,以后创建的工程也要基于demo工程去完善。**
# 四、初入ART_PI的经历
在我第一次用ART_PI的时候,我就已经下好了RT_Thread studio开发环境,并且根据官方的文档已经初步了解了一下RT_Thread studio的简单使用。因为我又是第一次使用操作系统,对这些概念一窍不通,加上之前裸机编程的思想,这猛一下的跳转觉实感觉吃不消。好在有B站这个神奇的网站,我在里面找到了RT_THread 推出的操作视频,花费了一天的时候全部过了一遍,感觉还很懵,听大概可能是听懂了吧,但是好像还是什么都不懂(笑死)。
RT_Thread 视频链接:[视频链接](https://www.bilibili.com/video/BV1JJ41167Lt)
这可怎么办,视频也看了,啥也干不了。没办法,继续找资料,什么地方资料最全,肯定是官网啊,于是,又去官网上继续学习和探索,在又简单的过了一遍后,终于有了个大概的了解。但是还是不够好,于是,我又跟着官网的每一章的介绍,跟着例程把每一个知识点都敲了一遍(可能不是全部),然后似懂非懂的我就准备开始做打印机这个项目了。
RT_Thread官网链接:[文档链接](https://www.rt-thread.org/document/site/)
# 五、熟悉阶段
在熟悉RT_Thread开发的过程中,我根据文档移植了SPI和QSPI,也就是对应开发板的两个外置flash,也写了按键和LED的例程。在CSDN上简单记录了一下。
一、[SPI](https://blog.csdn.net/lengyuefeng212/article/details/113848520)
二、[按键和LED](https://blog.csdn.net/lengyuefeng212/category_10808585.html?spm=1001.2014.3001.5482)
\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-下面是本章重点,打印机的调试和实现\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-
# 六、打印机工程的调试和实现
## 1.创建一个demo工程
这里不介绍了,建议去看官网文档。
## 2.制作和烧写字库
关于这一快,我也写了一个文档,里面是用STM32F103的芯片,并且是裸机工程,可以看制作部分,烧写部分我会在下面介绍。
[字库制作和烧写](https://blog.csdn.net/lengyuefeng212/article/details/113818704)
不想自己制作的可以直接去我的github下载
[github链接](https://github.com/lengyuefeng212/Word_Bank)
找到其中的![image.png](//image.lceda.cn/pullimage/goKjW44X8qiTfhw5maPzgBmSnhed2psT9kCJMISc.png)
这个里面就是测试好的字体,我使用的也是这种。
**下面介绍使用ART_PI烧写字库的方法**
完成这一步还需要两步,一步是串口的移植,一步是W25Q128的移植。
## 3. W25Q128的移植
如果是基于ART_PI创建的工程,这一步是不需要操作的。我竟然我从cubuMX中拷贝了SPI1的驱动,而且还放到了board.c文件中,(我真傻,后面会介绍)
开启宏定义和使能SPI1
然后挂载设备。
```
/*---------挂载SPI1设备--------------*/
int rt_spi_w25Q128_init(void)
{
/* 向系统注册SPI1总线上spi10从设备 */
rt_hw_spi_device_attach("spi1", W25Q128_DEVICE_NAME, GPIOA, GPIO_PIN_4); // W25Q64的片选连接在PA4上
// if(RT_NULL == rt_sfud_flash_probe("W25Q128", W25Q128_DEVICE_NAME)) // 检测这个设备
// {
// return -RT_ERROR; // 没有检测到
// }
return RT_EOK; // 检测成功
}
```
然后使用官方文档的初始化读ID的程序(去看官方例程)
读出来数据是
![image.png](//image.lceda.cn/pullimage/quc0wiHrJY5grazA5BIRgmUXC3JqPb7m2rKSwCJa.png)
然后用命令查看一下SPI10这个设备挂载成功
![image.png](//image.lceda.cn/pullimage/DWcaFg3bihXG9wOWsQz3ysDedQuvKgAMiCAuHnAg.png)
然后就是写W25Q128的芯片擦除,块擦除,扇区擦除,写数据和读数据函数。(根据野火例程移植)
简单贴个图,就不细说了。代码可贴在下面,请自行查阅。
![image.png](//image.lceda.cn/pullimage/5eZPCPfzsoJNbxJwYs9vvjKKzqw7nghmgdRoDYSS.png)
我在调试的过程中需要不断的去测试,看看数据有没有烧写进去,于是我就写了一个读数据函数并且添加到了命令行。
```
/* 测试读数据 */
void aa(int argc,char *argv[])
{
rt_uint16_t i;
rt_uint8_t * pBuffer;
rt_uint32_t readAddr = 0; // 读地址
rt_uint16_t length = 0;
/* 配置接收的参数 */
rt_kprintf("read argv[1]:%s \n",argv[1]);
readAddr = htoi(argv[1]); // 将16进制字符串转化为整形 // 要读的地址
length = atoi(argv[2]); // 要读的长度
rt_kprintf("readAddr:%x length:%d \n",readAddr,length);
SPI_FLASH_BufferRead(pBuffer,readAddr,length);
for(i = 0; i< length;i++)
rt_kprintf("%x ",pBuffer[i]);
}
```
这个函数可以接受命令行的参数,来确定要读的数据的首地址和要读数据的字节数。
这里不得不介绍两个函数
\> atoi() 这是自带的 是将字符串的数字转化为整数
\> htoi() 这是需要自己写的,是将字符串的16进制转化为整形 在地址这里非常好用。
htoi这个函数是从网上找的,感谢这位提供的朋友。
```
/*---------------------------函数 */
rt_uint16_t tolower(rt_uint16_t c)
{
if (c >= 'A' && c <= 'Z')
{
return c + 'a' - 'A';
}
else
{
return c;
}
}
rt_uint32_t htoi(char s[])
{
rt_uint16_t i;
rt_uint32_t n = 0;
if (s[0] == '0' && (s[1]=='x' || s[1]=='X'))
{
i = 2;
}
else
{
i = 0;
}
for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)
{
if (tolower(s[i]) > '9')
{
n = 16 * n + (10 + tolower(s[i]) - 'a');
}
else
{
n = 16 * n + (tolower(s[i]) - '0');
}
}
return n;
}
```
测试:
在命令行输入
`aa 0 256 就是从0地址处读256个字节`
![image.png](//image.lceda.cn/pullimage/XkOlb3nW2ej87302t0IIXeFkpjbRnXO8evPuNnD1.png)
写入我也是封装了一个这样的函数。篇幅有限,就不演示了。
至此,W25Q128移植成功。
## 4.串口的移植
因为我需要用串口发送字库,然后写入到W25Q128里,这就得需要移植串口的程序,因为我的扩展板引出了串口1,那就移植串口1.
在board.h中使能串口1
![image.png](//image.lceda.cn/pullimage/hi0lhIZ8Vr34TBPBhq8GZkLonjmIfzf1o1lebhO9.png)
我使用的是官方的中断例程,本来想使用DMA传输的,但是不知道为啥不能用,那就没办法了,中断就中断吧,简单修改一下串口接收回调函数,用一个数组保存每次接收的数据,每接收256个字节就写入一次flash中
修改后的程序
```
/*------串口接收线程入口-----------------*/
void uart1_recv_thread_entry_INT(void *parameter)
{
char ch;
rt_uint8_t buf[300];
rt_uint16_t buf_length = 0;
rt_uint32_t writeAddr = 0; // 写入flash的首地址
while(1)
{
/* 从串口读取一个字节的数据,然后读取到则等待接收信号量 */
while(rt_device_read(uart_dev_usart1, 0, &ch, 1) != 1)
{
/* 读取不成功,等待获取信号量 一获取信号量就表示有数据接收 */
rt_sem_take(&rx_sem, RT_WAITING_FOREVER); // 阻塞等待接收信号量
}
/* 读取到的数据通过串口错位输入 */
//ch = ch + 1;
//rt_device_write(uart_dev_usart1, 0, &ch, 1);
/* 把接收到的每一个字符都写入到flash中 */
buf[buf_length ++] = ch; // 存储字符到数组
if(buf_length == 256) // 一帧数据是256个字节
{
// 一帧数据接收完,先写入flash中
SPI_FLASH_BufferWrite(buf,writeAddr,buf_length); // 往flash 中写入数据
writeAddr += 256;
buf_length = 0;
rt_memset(buf, 0, 256); // 清空缓冲区
rt_kprintf("write 256 byte success!\n");
}
}
}
```
每次接收256个字节都会在控制台打印"write 256 byte success!\n") 如此还能查看运行的状态
串口助手的配置请看上面的字库制作和烧写,是一样的。
如此串口也配置完成,
烧写:
先擦除整个芯片,这里有一个问题,就是我用擦除整片芯片的命令去擦除芯片,读出来的数据也是0xff,但是就是前面大概两三个块是写不进去数据的,后面的能够写入数据,不知道什么原因(有知道的还望指导一二)。
最后实在没办法, 我就用一个循环去擦除每一个块,这样间接擦除了整个芯片,效果是不错的,数据写入也正常。
```
/*----------用块擦除代替芯片擦除---------- */
/* 一共是16MB 16384KB 一个块64KB 一共是256个块
* 一个块的起始地址和结束地址范围是 0-65536B 也就是0-FFFF
* 0-FFFF 10000-1FFFF 20000-2FFFF ...
* */
void SPI_FLASH_BlockFormat(void)
{
rt_uint16_t i;
rt_uint32_t BlockAddr = 0; // 地址
rt_kprintf("SPI_FLASH_BlockFormat start !\n");
for(i = 0; i< 256;i++) // 一共擦除256个块
{
SPI_FLASH_WriteEnable(); // 写使能
SPI_FLASH_WaitForWriteEnd(); // 等待写完成
// 写入4个字节的数据 不接收
W25Q128_Send_Buf[0] = W25X_BlockErase; // 擦除块指令
W25Q128_Send_Buf[1] = (BlockAddr & 0xFF0000) >> 16; // 块地址
W25Q128_Send_Buf[2] = (BlockAddr & 0xFF00) >> 8;
W25Q128_Send_Buf[3] = BlockAddr & 0xFF;
rt_spi_send(spi_dev_w25q128,W25Q128_Send_Buf,4);
SPI_FLASH_WaitForWriteEnd(); // 等待写完成
BlockAddr += 10000; // 一次加10000,也是一个块的首地址
}
rt_kprintf("SPI_FLASH_BlockFormat complish !\n");
}
MSH_CMD_EXPORT(SPI_FLASH_BlockFormat,SPI_FLASH_BlockFormat); // 添加到命令行
```
如此,字库写入成功。
验证:
我还写了一个函数去读取字库中的字节数,看看是否数据是否一致。
代码太长,贴一个图
![image.png](//image.lceda.cn/pullimage/usKJdF9cLh6jBMEdpxdJj5PzDXgijAru0bmz8ai2.png)
![image.png](//image.lceda.cn/pullimage/CbEkaE8E9Vue8s4ChFEJDK05SZne1rJeZa22vTV4.png)
在命令行执行测试整个命令,将会把你这个字模的数据取出来,我这里是33*32的字体,一共是132个字节
![image.png](//image.lceda.cn/pullimage/rD2rHBHLMtEehfYaGBP1hJm7Ai1mpx1DUdOXgFee.png)
用winhex即可验证正确性,在字库制作和烧写有介绍,这里就不多说。
~~如此,字库烧写完成。~~
# 打印机驱动移植
简单介绍打印机的引脚
4个引脚控制电机的运行,6个引脚负责加热,一个引脚LAT负责锁存数据(说白了就是片选),一个SCK,一个DI(可组成一个SPI)
如此,只需要把4个点击引脚和6个加热引脚配置为PIN输出,LAT和SCK和DI配置一个SPI设备用来传输数据,如此便可驱动。
在配置过程中需要的问题,我和往常一样去从CubeMX中配置好SPI2的驱动,然后移植到board.c文件中,也使能了SPI2,在控制台也识别了SPI20设备,但是最后的结果就是SPI传输失败。
在这一步耽搁了很长时间,各种测试,各种找错误,还用逻辑分析仪测试了一下,什么波形也没有,毫无头绪。最后看了网上一遍文档,才知道我从一开始就错了,(说我是猪头不冤)
基于开发板创建的项目,我们在board.c添加移植的驱动是没有用的,我们需要在
![image.png](//image.lceda.cn/pullimage/57DISMRF533lsQTprfCYhw2vXP0gR8lhGmguAcSB.png)
这个文件中去配置
我打开了这个文件,发现了SPI的初始化,里面有SPI1和SPI4的驱动,这也就解释了为什么我的第一步W25Q128能驱动成功,而我的SPI2传输数据失败,
在我移植了SPI2的程序之后
![image.png](//image.lceda.cn/pullimage/XnjnFvs0MD7wzsf9MSLn3xvjyA2m8kp4NKzKauuu.png)
果然,传输正常了。
测试电机转动:
测试电机转动只需要让4个引脚依次输出电平,也就是驱动步进电机,
我写了一个转动的测试,
```
/* 测试打印机转动 */
void testPrinterRight(void)
{
rt_uint16_t i;
for(i = 0;i < 40; i++)
{
printer_right(50,2);
}
rt_kprintf("testPrinterRight success\n");
}
MSH_CMD_EXPORT(testPrinterRight,testPrinterRight);
```
只需要在命令行输入testPrinterRight
电机就会转动一段距离。
再写好打印汉字,字母的程序,打印图片的程序,经过不懈的努力,第一阶段算是完成了。
贴上效果图:
![image.png](//image.lceda.cn/pullimage/N3J5zq9HvlLqEPEzpA6xHO6FS17XmO5MXz4b8Etu.png)
![image.png](//image.lceda.cn/pullimage/OyIST58h7SIvnJjrGaVTwNTk9dZBD7Gzdpwzxdwd.png)
汉字感觉还是可以的,就是图片有点惨不忍睹。
后续会继续完善的。
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
# 第二阶段 2021-3-22
这是第二阶段的开发和测试了。因为现在手里有好几个项目,不能全心全意的去开发这个项目,只能通过抽点时间这种方式间接的去完成这个项目。(因此有很多的功能都没有开发出来,还请见谅,不过我会一直开发的)
## 1.增加的功能
在第一阶段的效果上,进一步修改,
>
1.改善了打印汉字不清楚的情况
2.增加了多行打印的情况,(原来只支持单行打印)
3.增加了屏幕显示汉字,然后点击对应的汉字可以去打印
本来时想要加APP或者是用屏幕直接拼写汉字去打印,但是都因为一些困难的因素而中断,转而去用这种很不方便的方法。
因为我以前习惯用esp8266去做为WIFI开发,现在ART_pi这个板子上的WIFI模板没有接触过,也不太熟悉,暂时还没有去研究,等以后有时间再去搞,那样的话,APP就可以开发使用了。
屏幕打字这个因为我不太熟悉拼音输入法打字,也没有系统的学过,网上搜了一下,好像感觉也不是很复杂,还没有去整,希望以后会把这个功能也加上去,
暂时就这个样子吧,等有空一定再回来好好的整整(一定)。
# 打印效果
![多种字体.jpg](//image.lceda.cn/pullimage/qqrQd8LfaSWJQDKpRC9Cte9s5GuT8bIXznYRcaB0.jpeg)
![多种字体1.jpg](//image.lceda.cn/pullimage/8BOVVZOqG34x66TxxH5o1CP8RcKBTRXrozLmnCkU.jpeg)
识别的二维码
![二维码.jpg](//image.lceda.cn/pullimage/NnD9X7Tbupd53Ew9Zoj6iAbp6yTrwMiybfWoFHoT.jpeg)
![识别二维码.jpg](//image.lceda.cn/pullimage/SVQQtsEVwRfcn7rAbmyZCoDHyPaFKvVmzEy9rFB9.jpeg)
# 测试结果见附件。
ID |
Name |
Designator |
Footprint |
Quantity |
1 |
24K |
R4 |
R0805 |
1 |
2 |
680 |
R10,R11,R12 |
R0805 |
3 |
3 |
LED-0805_R |
LED1,LED4,LED2,LED3 |
LED0805_RED |
4 |
4 |
Header-Female-2.54_2x20 |
P1 |
HDR-TH_40P-P2.54-V-R2-C20-S2.54 |
1 |
5 |
SK12D07VG5 |
SW1 |
SW-TH_SK12D07VG5 |
1 |
6 |
K2-6639DP-B4SW-04 |
KEY1,KEY2,KEY3 |
KEY-TH_4P-L6.0-W6.0-P4.50-LS7.5 |
3 |
7 |
100nF |
C1,C2,C3,C4,C6 |
C0805 |
5 |
8 |
56K |
R2 |
R0805 |
1 |
9 |
micro USB |
USB3 |
MICRO-USB-SMD_5P-P0.65-H-F_C10418 |
1 |
10 |
FTP-628MCL101 |
U2 |
FPC-1.0MM-LSXJ-30P |
1 |
11 |
TSA343G00D-250J2 |
SW2 |
SW-SMD_L4.0-W2.9-P4.15-EH |
1 |
12 |
2.54mm 2*20P |
P2 |
HDR-TH_40P-P2.54-V-M-R2-C20-S2.54 |
1 |
13 |
22K |
R3 |
R0805 |
1 |
14 |
M3固定孔 |
M8,M7,M6,M5 |
M3_3.5MM |
4 |
15 |
LM393 |
U1 |
SOP-8_L4.9-W3.9-P1.27-LS6.0-BL |
1 |
16 |
10K |
R1,R5,R6,R9,R7,R8 |
R0805 |
6 |
17 |
LB1836M-TLM-E |
U4 |
SOIC-14_L8.7-W3.9-P1.27-LS6.0-BL |
1 |
展开
5
12
收藏到专辑