前言
这一周做出来了一个桌面小时钟,跟着rtt官网的教程,很快就完成了使用esp8266获取NTP时间教程。不得不说,rtt的各种软件包真的好用,一步到位。接下来讲一讲开发过程中遇到的问题。
坑
显示图片花屏
一开始,我本来是想让屏幕显示一张图片的大小,但是发现显示出来有差不多2/3的部分花屏,一部分正常显示。上百度查了一下,有人说可能是屏幕硬件问题,但是我的刷屏函数是可以让屏幕正常显示白色的,所以排除硬件问题;而且由于能刷屏,那么应该也不是SPI传输的问题。
抱着试试的心态,我修改了显示范围,使屏幕的显示范围为两张图片的大小,结果如图。
王的发??为什么SPI会传输一堆乱码然后再传数组里的数据??思考了一下,应该是spi在定位或者读取数组数据这个环节出了问题。我想到我的图片数组是单独存放在一个.c文件里的,如果我把它放在传输函数同一文件下,会不会有改变呢?试了一下,没想到。
我佛咯,这都有关系。这个办法虽然能解决问题,但是对于使用240*320屏幕的单片机来说,一张全屏的16位真彩色图片就要一百多KB,实在太浪费空间了。这应该可以采用SD卡+SPI的方式显示来解决。
图片斜向显示
如图。
本来应该显示一个’0’,但是却显示成了这样,明明函数啥的都没改过,为何会显示成斜的呢?想了想,上面那个成功显示的图片,两边都接触到了屏幕的边缘,也就是说全屏。那这两个显示的区别就在于,一个的显示区域固定住了或者说屏幕宽度上限了,而另一个还可以扩展。那会不会是设置显示区域的问题?思来想去,起始位置和结束位置定义错了!佛辣!如果要在横坐标60-90的区域显示,那么横坐标X1应该设置为60,而X2应该设置成89,没想到setaddress函数的参数里真写了60和90。崩贵。
SPI设备在不同线程里的使用
上面显示图片的例子都是在main函数里完成的,由于要区别裸机开发,就要在创建线程,在线程里编写接下来的步骤。但是在自己创建的线程里使用SPI传输时,却不能正常显示图片,串口也并没有输出调试信息,输入list_device也有spi10(我使用的spi设备)。这是为何?难道不能在不同线程里使用同一个spi设备?在程序中加入几个rt_kprintf语句,确定了程序是在rt_spi_transfer_message_my这个函数里卡住了。(CSDN里大佬修改的spi_dma传输函数)
struct rt_spi_message rt_spi_transfer_message_my(struct rt_spi_device device,
struct rt_spi_message *message, rt_sem_t sem)
{
rt_err_t result;
struct rt_spi_message *index;
RT_ASSERT(device != RT_NULL);
/ get first message /
index = message;
if (index == RT_NULL)
return index;
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(-RT_EBUSY);
return index;
}
/ reset errno /
rt_set_errno(RT_EOK);
/ configure SPI bus /
if (device->bus->owner != device)
{
result = device->bus->ops->configure(device, &device->config);
if (result == RT_EOK)
{
device->bus->owner = device;
}
else
{
rt_set_errno(-RT_EIO);
goto __exit;
}
}
/ transmit each SPI message /
while (index != RT_NULL)
{
result = device->bus->ops->xfer_my(device, index, sem);
if (result == 0)
{
rt_set_errno(-RT_EIO);
break;
}
index = index->next;
}
__exit:
/ release bus lock /
rt_mutex_release(&(device->bus->lock));
return index;
}
在rtt studio里运行仿真,发现程序运行到这一句时卡住了:
result = rt_mutex_take(&(device->bus->lock), RT_WAITING_FOREVER);
单步跳入rt_mutex_take查看,发现spi设备要在不同线程使用,不仅仅是将spi设备挂载到spi总线上那么简单。
我在上一篇文章的代码中写道,
rt_spi_take_bus(&spi10);
这一步很关键,它获取了spi总线的控制权,实际上是获取spi设备所挂载到的总线上的lock这个互斥锁的值,没有这一步,spi就不能正常使用。而在使用rt_spi_transfer_message_my函数时,会先获取spi设备所挂载到的总线上lock的值,获取过程是:
+ 获取当前线程的句柄。
/get current thread /
thread = rt_thread_self();
+ 若当前线程是此互斥锁的owner,那么无论lock.value的值是多少,都获取成功。
if (mutex->owner == thread){
if(mutex->hold < RT_MUTEX_HOLD_MAX){
/ it’s the same thread /
mutex->hold ++;
/进入到这个if后,直接运行到函数尾/
}
else …..
}
…..
return RT_EOK;
若当前线程非互斥锁的owner,查看lock.value,若value>0,则value—,且设置owner为当前线程,并获取成功。
问题应该很明显了,那就是由于我在main函数里调用了rt_spi_take_bus函数,此时spi总线的lock的值为0,owner为main,而在线程里调用传输函数时,由于lock.value == 0,并且owner也不是该线程,于是程序就一直卡在互斥锁的获取这一步。
解决方法很简单,只需要在main函数结束spi传输的地方加上一句:
rt_spi_release_bus(&spi10);/使spi10让出spi1总线控制权/
然后在线程开始的地方加上:
rt_spi_take_bus(&spi10);
就可以在线程里正常使用spi设备了。(同理,若要在另一个线程里使用,也要重复上面的步骤)
原作者:Budali11