Code Monkey home page Code Monkey logo

ldc-clock's Introduction

ldc-clock

摘要

这是为了完成这次个人任务完成的项目,能编译一个用于合宙esp32c3加上合宙的ldc拓展板的固件。使用时钟中断懒加载屏幕显示内容,使用两个串口中断实现手动调节时间的功能,编译时写入时间。代码:https://github.com/nan-mu/ldc-clock

问题与解决方案

与其走马观花地介绍一遍代码,我认为这里更应该记录一下途中遇到的问题和解决方法。

屏幕驱动

屏幕的芯片是st7735,通信协议是spi,这是淘宝店唯一能够提供的信息。而能找到的软件包是embedded_graphics,具体编写时使用该库的st7735实现。具体编写时遇到两个问题,前面说到淘宝店唯一能够提供的是芯片信号,他们推荐使用自己的RTOS编写程序,所以一开始花了很多的时间查看原理图,反复调试得到屏幕坐标上的偏移量和屏幕尺寸。网上有说法说这个屏幕大小是80*160,实际得到110*161。偏移量有说法是12,14大概,实际得到1,26。得到屏幕大小的方法是全屏刷新颜色,白,红,黑顺着来只要前一个颜色没有完全被覆盖就是屏幕大小没有调对。偏移量的方法是在理论的屏幕边界上画线。一开始做的时候有些拖沓加者有在忙别的事,花费了比较久时间。

问题二是刷新方式,合理的解决方案还是在写报告时发现的。但当时认为屏幕刷新太慢,担心影响到时间精度(实际每10秒就会产生1秒的误差),所以花很多时间在编写消耗资源最小的显示算法上。主要尝试了这几种:

  • 加载两种文字颜色,将擦除操作改为在文字对应的点上写入背景颜色。实际使用时发现有两个问题,一个是写入对象没有暴露出对应的接口来修改颜色,需要重新构建一个新的样式来写入背景颜色的文字。这会有一个问题,绘制流程依赖于一个文字对象,文字对象持有而不是借用一个样式对象,所以假如我们希望只修改文字颜色,那么每次操作都需要在文字对象上写入两次样式对象。
  • 当时我认为应该从另一个方面提升性能。之前使用过ssd1306,每一次更新信息会重新绘制所有的内容,但时钟的应用场景下只有进位发生的是否才需要更新更多的内容。所以本来一个时间字符串改成了6个独立的数字,使用一个时间结构体做加时操作,该操作能够返回一个动态数组,数组中存储标志位,绘制部分根据标志位更新对应位置的显示。
  • 最后我一直在观察刷新时屏幕的变化,发现似乎直接更新一块区域的像素会比一个一个更新文字所在的像素点要快,所以之前提到的两次写入样式带来的消耗就消失了。

而最后的最后,我发现了导致卡顿的真正原因:spi频率设低了🤡

中断

一开始我使用延时函数和死循环更新显示,而根据Rtc时钟的输出发现延时函数的精度也难以忽略。所以这期间有换成了中断的写法。而这个使用场景下需要耗费大量的经历去人工推断类型,因为静态变量要求指明对象的类型,rust的嵌入式编程中使用大量的泛型来保证对象的原子性。另一个问题是静态变量有着非常复杂的机制来提供跨域的所有权传递。因为中断函数的作用域与main函数是不接壤的,所以我们只能在main函数完成一个静态对象的初始化。而静态对象需要在创建时初始化,也就意味着我们需要用一些中间对象提供来完成静态对象的初始化但将真正的初始化在main函数完成。而根据我以往的经验,只要对象实现了default特性,我们一个在初始化时创建一个真正的,无依无靠的静态对象。而在main函数里我们不安全地得到对象的所有权(因为静态对象的生命周期是一直在的,所以可以在所有作用域下得到所有权而不消耗所有权,而期间具体是何种原理我还没搞清楚,看起来使用unsafe块后对象有两个所有权了)。当然这样做编译器已经告诉你是不安全的了,所以一般会使用其他方法,问题就在于这个方法太多了,以前使用Mutex+Arc,而乐鑫官方的中断示例代码中使用MaybeUninit。他们在实现需求上是一样的可具体的代码却有很大不同。实际体验下来,前者更加适合异步环境,因为Arc要求显式地分发操作对象,这样我们能在异步函数中间传递这个对象。所以它的作用域是和对象的生命周期相关,在一些特殊项目中较难管理。比如嵌入式中main函数需要初始化所有外设,但我们通常希望每一个外设的控制对象不要贯穿在整个main函数的作用域当中。这种情况下使用后者更加合适,MaybeUninit要求对对象的读写操作在一个闭包内完成,所以它的生命周期通过闭包捕获就不会蔓延的整个函数内了。除此之外我还在一个非嵌入式的裸机场景下见过两种不同的实现,所以这个问题是很复杂的。

另一个问题是esp32c3似乎没有特定的gpio中断,所有gpio共用一个中断函数但有不同的中断标志位,我一直感觉是我漏掉了一些资料,它甚至后好几个软件中断和多个时钟中断。所以我们只能手动的查看到底是那个引脚在触发。奇怪的是在esp32c3C语言的教程当中说有一个寄存器会表明触发源,而在rust中我找了很久都没有找到这个寄存器的接口。而官方仓库中的示例也没有这种使用两个引脚中断的例子。最后只得在中断函数中查看端口电平。

授时

一开始查资料的时候如何授时是个问题,最后的解决方法是在在编译之前运行一个脚本,脚本往一个文件里写入时间。而写入时间的数据结构困扰了我很久。一开始我计划写入UNIX时间戳,但解析这个时间戳却是个问题。在以往直接使用时间相关库就行,但现存的嵌入式时间库当中都剔除了这部分了功能,只提供时间的度量和一些基本操作,没有解析时间戳。第二个方案就是接入wifi,可惜上次问老师如何接入CUG时老师既没有回答也似乎比较排斥这种方案所以到现在我也没时间研究出结果。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.