要说SSD1306的OLED小屏幕,现在无论是AI还是萌新,点亮起来毫不费力了。本次测评保留上一篇开始的外设,再增加一个I2C和GFXTIM来做一个牛刀杀鸡的小实验。
* k& g8 F$ ~$ q: e5 D' h$ J) Z5 Y4 L5 ]- j
先说GFXTIM 的优势,这是一个显示时序专用外设,像定时器又不全是的一个新外设。可以精确到像素级事件触发和状态捕获,简单说,这个东西可以将显示环境变得可控,更具备确定性。那对于I2C接口的OLED有啥用呢,当然是只用作了刷新节拍。牛刀有了,当前只有鸡,先杀了再说。- G- x% n, {4 o. s* V; x+ i
屏幕的速率有限,但I2C的主机要拉高,直接高速模式整上,DMA也安排
9 B; k" R0 ]' i; K6 `8 E( n
5 O$ ]3 V4 j2 i/ ^$ m! T" x7 C
有了以上,就可以填代码去试着点亮屏幕了。DMA把数据送到缓冲区
}$ }( ?. i2 s # @8 ^* G) D+ a1 r# O
- HAL_StatusTypeDef ssd1306_refresh(void)
* G1 E4 B3 O4 h5 A0 C% ? - {
: S5 G: x2 Q# o- a* O5 G, N - HAL_StatusTypeDef ret;
5 A Y) k# S/ k) p
9 l1 Y: S6 d: ^; N! Z8 E3 }+ _- if (s_i2c == NULL)9 B8 E7 @4 }. K/ J4 E
- {3 A7 _7 }* d+ M% m; h
- return HAL_ERROR;
+ ?2 l* U! x; a, W - }
8 X9 h! O* N y$ b - if (s_busy)
) c( H1 x; o, G$ ^2 \+ ?3 }$ O - {
( W/ [! r# i7 ]' C - return HAL_BUSY; /* 上一帧还在送,调用方下一轮再来 */: `9 g+ T( l$ t1 H
- }
- Y! s- w- [6 ?/ H8 i; h; X3 s" s - ( l0 t+ c" k1 x" b9 R! V
- /* 整屏窗口:列 0..127,页 0..7,之后的数据流会自动铺满全屏 */4 }3 M1 c; a i6 }
- ret = set_window(0u, (uint8_t)(SSD1306_COLS - 1u), 0u, (uint8_t)(SSD1306_NPAGES - 1u));
) S. L3 Y6 j% W0 v J - if (ret != HAL_OK)
6 {" e- a% n2 v9 ?+ f4 O+ @# C - {
; i' A$ Q" W% B; ? - s_stats.errors++;
* s( `) G3 d, O - return ret;
3 `9 q0 W$ ^3 p Y4 T# X - }5 W5 h. d* v$ {, v7 i
- / c0 ~- X3 C+ H0 q& q Q
- s_frame_tick = HAL_GetTick();' o# n$ G' B. O6 A
- s_busy = true;
) U1 q& n' w& N, i) J1 F
6 ?$ T$ J* {9 A- /* 一次 DMA 送出 "0x40 + 1024 字节显存",零拷贝 */3 E6 B8 `/ M0 } A
- ret = HAL_I2C_Master_Transmit_DMA(s_i2c, SSD1306_ADDR8, s_vram,, g* R9 r0 K9 P/ g4 V# M( |6 _% L
- (uint16_t)(1u + SSD1306_FB_SIZE)); a8 W$ f- x$ g8 f" L
- if (ret != HAL_OK)
) ]9 I1 C- g! T: q: Z- z9 | - {6 U/ @/ \0 ~% u# B& J
- s_busy = false;
8 M+ i8 h5 [; L( G - s_stats.errors++;$ m9 e* w( H' O. ]$ J- Y, l
- }% |) R( a: L1 Q6 w' }5 w4 t
- ; n# b( \, [- Y/ e0 p0 H5 q
- return ret;
) N. T' B4 }$ D7 o! ~& S, l* v - }
! B* i. _) ]- I% Q& e0 @0 A
复制代码 GFXTIM 的周期完全由硬件计数器和自装载决定,软件只负责读计数。无论主循环忙成什么样,20 Hz 的节拍始终是 20 Hz,不会因为某帧多算了 3 ms 就整体后移。这对刷新率稳定性和测量类 UI(走秒、波形时间轴)是绝对优势。
- C4 K, F6 v1 i5 j8 w' ^" w E 7 Q v* k1 V% ^& ]4 C4 m
) O& j( B) [- @$ A S) B5 v2 h
7 o% ?0 b, d3 F G
- uint32_t tick = gfx_tick_count();
; r* U# Z: a. b/ `9 Q7 e0 U5 N( e7 Q - ; F2 f% ^0 N3 ~! Q) J* _- B; f
- if (tick != last_tick)9 ^" {, _, [: E8 j, j/ l6 y2 y
- {& J9 J+ Z" X$ o
- last_tick = tick;* V( x/ \% r) B2 C9 C: q
- 3 M0 j7 d8 N5 Q! h' N
- if (!ssd1306_is_busy())
* Q% [, \( x. r! I/ n! v - {
8 c v# j8 z$ }) d" p - temperature = __HAL_ADC_CALC_TEMPERATURE(3300, adc_raw[0], ADC_RESOLUTION_12B);. |3 d9 l! k) R& S5 X6 c8 u
- ui_draw(now, temperature);+ ?; O5 b9 Q# o3 {/ p0 X3 [( v( i
- (void)ssd1306_refresh();
8 k1 t$ b9 n" z - }7 g7 ]3 i; u- N% b7 @2 \0 K! z- j
- }
复制代码 这里面最慢的就是刷屏速度,如果上一次没消费完就跳过当前的这一次。实际跑起来还是很稳的。 并且上一次开启的内部温度测量和串口外设也在保留,再一次验证了多外设同时工作的稳定性。
3 ?+ P5 s) S3 g# E( x
: Q: [/ L7 q. i. B) m- v- w4 H* Z 这个开发板挺好用的,还有其他外设没加进来,继续搞。当前这个小实验实拍如下4 L, G, N2 U- e- Q2 w+ i* [: t
$ ^7 j7 z- k$ w7 f
0 o" r6 f6 S5 @1 l& R; E- c
|