Skip to content

Commit 7a5107d

Browse files
committed
add: ws2812
1 parent 50c8a7c commit 7a5107d

7 files changed

Lines changed: 158 additions & 8 deletions

File tree

docs/.vuepress/sidebar/zh.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ export const zhSidebar = sidebar({
2020
icon: "lightbulb",
2121
},
2222
{
23-
text: "API 参考",
23+
text: "开发上手",
2424
collapsible: true,
25-
prefix: "library/",
25+
prefix: "tutorial-advanced/",
2626
children: "structure",
27-
icon: "book",
27+
icon: "magic",
2828
},
2929
{
30-
text: "开发上手",
30+
text: "API 参考",
3131
collapsible: true,
32-
prefix: "tutorial-advanced/",
32+
prefix: "library/",
3333
children: "structure",
34-
icon: "magic",
34+
icon: "book",
3535
},
3636
{
3737
text: "应用示例",

docs/.vuepress/theme.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export default hopeTheme({
99

1010
logo: "logo.svg",
1111

12-
repo: "Air-duino/document",
12+
repo: "Air-duino/Arduino-AirMCU",
1313

1414
docsDir: "docs",
1515

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ bgImageDark: /bg/6-dark.svg
88
bgImageStyle:
99
background-attachment: fixed
1010
heroText: Arduino AirMCU
11-
tagline: AirMCU 是一个 AirM2M 公司 ARM-Cortex 架构微处理器的兼容 Arduino 开发平台
11+
tagline: AirMCU 是一个合宙(AirM2MARM-Cortex 架构微处理器的兼容 Arduino 开发平台
1212
actions:
1313
- text: 现在开始 🚀
1414
link: ./getting_started/
34 KB
Loading
167 KB
Loading
405 KB
Loading

docs/tutorial-extras/ws2812.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
---
2+
title: 彩色灯珠 WS2812
3+
order: 11
4+
icon: braille
5+
---
6+
7+
## 简介
8+
9+
本章介绍使用Air001开发板驱动 WS2812 灯。
10+
11+
::: tip
12+
13+
[WS2812 是一个三色 LED 驱动芯片](http://world-semi.com/ws2812-family/),一般集成在 LED 灯珠中。
14+
它仅需一个 IO 传输数据,可通过多个 LED 串联传递信号,实现批量控制。
15+
16+
:::
17+
18+
## 硬件准备
19+
20+
-[☁️ Air001开发板入门](/tutorial-advanced/Air001_start.html),将`Air001``DAPLink调试器`使用排针排母连接。
21+
22+
-`Air001开发板`的 USB 插入一根数据线,用向灯板提供 5V 电压。
23+
24+
-`WS2812`LED 模块与`Air001开发板`,按如下表格进行相连:
25+
26+
| WS2812 模块 | Air001 |
27+
| :---------: | :------: |
28+
| GND | GND |
29+
| VCC | VBUS(5V) |
30+
| DIN | PA_7 |
31+
32+
::: tip
33+
34+
由于 WS2812 的时序要求相对严格,我们将使用 SPI 的 MOSI 引脚(PA_7)对其进行驱动。
35+
36+
:::
37+
38+
## 软件部分
39+
40+
查阅资料,我们可以得知`WS2812`需要严格的时序,我们将参考[此处](https://github.com/452/arduino-ws2812-direct-spi-control)的代码,使用 SPI 进行驱动。
41+
42+
:::details WS2812 的控制时序,此处不作深入讲解
43+
44+
![文档时序内容](img/ws2812-ds.png)
45+
46+
:::
47+
48+
首先,**为了保证 SPI 频率在 8MHz,我们需要将芯片的主频设置为 16M**,这样只要设置 SPI 二分频即可实现输出为 8MHz。
49+
50+
:::details 在Arduino中设置芯片主频
51+
52+
![设置芯片主频](img/QQ20230811140354.png)
53+
54+
:::
55+
56+
我们在代码开头,引用库与定义需要使用的全局量:
57+
58+
```cpp
59+
#include<SPI.h>
60+
//LED灯的个数
61+
#define LED_N 64
62+
//用于存储当前LED灯的状态,默认全为(0,0,0)不亮
63+
unsigned char LED_T[LED_N][3] = {0};
64+
```
65+
66+
这里的`LED_T`用于存储当前的LED灯颜色数据,可以在刷新时一次性将所有状态全部发给`WS2812`。
67+
68+
在`setup`初始化函数中,我们初始化一下`SPI`,并将灯的状态初始化(全部熄灭):
69+
70+
```cpp
71+
void setup (void) {
72+
//初始化SPI
73+
SPI.begin();
74+
SPI.setBitOrder(MSBFIRST);
75+
SPI.setDataMode(SPI_MODE1);
76+
// 这里需要让SPI处于8MHz
77+
// 所以芯片要设置16M,SPI配置为2分频:
78+
// 16/2=8MHz
79+
SPI.setClockDivider(SPI_CLOCK_DIV2);
80+
delay(10);
81+
//刷新一下所有灯的状态,函数见文档接下来的内容
82+
WS2812_refresh();
83+
}
84+
```
85+
86+
这里的重点是`SPI.setClockDivider`函数,它设置了 SPI 频率在 8MHz。
87+
`SPI.setBitOrder``SPI.setDataMode`的配置,也保证了后续模拟时序的正确性。
88+
89+
接下来是真正模拟`WS2812`时序的函数,这个函数传入 RGB 值,转换为信号进行发送:
90+
91+
```cpp
92+
void WS2812_send(unsigned char r, unsigned char g, unsigned char b) {
93+
unsigned char bits = 24;
94+
unsigned long value = 0x00000000;
95+
value = (((unsigned long)g << 16) | ((unsigned long)r << 8) | ((unsigned long)b));
96+
while (bits > 0) {
97+
if ((value & 0x800000) != LOW) {
98+
SPI.transfer(0xF8);//1
99+
asm("nop");
100+
asm("nop");
101+
} else {
102+
SPI.transfer(0xC0);//0
103+
}
104+
value <<= 1;
105+
bits--;
106+
}
107+
}
108+
```
109+
110+
由于`WS2812`灯珠是串在一起进行通信的,当需要控制所有灯珠时,只要将颜色数据一个个发送出去就可以了。
111+
所以`WS2812_refresh`刷新函数就像下面这样,依次发送颜色数据:
112+
113+
```cpp
114+
void WS2812_refresh() {
115+
unsigned int n = 0;
116+
for (n = 0; n < LED_N; n++) {
117+
WS2812_send(LED_T[n][0], LED_T[n][1], LED_T[n][2]);
118+
}
119+
delayMicroseconds(60);
120+
}
121+
```
122+
123+
最后我们在`loop`函数中,简单写一个按顺序亮灯的小功能:
124+
125+
```cpp
126+
int count = 0;
127+
void loop(void) {
128+
count++;
129+
//把上一次亮的灯灭了
130+
LED_T[(count-1)%LED_N][0] = 0;
131+
LED_T[(count-1)%LED_N][1] = 0;
132+
LED_T[(count-1)%LED_N][2] = 0;
133+
//电亮这次的这个灯
134+
//来个浅蓝色吧
135+
//亮度低一点,不然刺眼
136+
LED_T[count%LED_N][0] = 5; //R
137+
LED_T[count%LED_N][1] = 5; //G
138+
LED_T[count%LED_N][2] = 20;//B
139+
//刷新所有灯的状态
140+
WS2812_refresh();
141+
//延时一小会
142+
delay(10);
143+
}
144+
```
145+
146+
## 输出结果
147+
148+
可以看到,灯会按顺序闪烁:
149+
150+
![灯按顺序亮起](img/ws2812-show.gif)

0 commit comments

Comments
 (0)