你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32 HAL库使用SPI+DMA驱动WS2812优化方案  

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
; m4 |+ d# b# n1 _& X. r
: J$ ?# r8 c& c3 e看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html  W% X6 a& b' H
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。% y: P% ^3 ~9 U3 g2 j( G' [6 s' i
2017-01-21 09_12_30的屏幕截图.png
) T" }' c# S8 Q6 J" `  E8 Q) c$ }  |- a+ d/ `; y8 Z
仔细一看发现 WS2812一个bit 周期是:
. J  L) ~4 P- t' }8 h9 H( J# z         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns9 ~* w* P3 _0 D1 d& a/ Z
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
4 y) w5 n1 y1 I1 k! E3 RSPI=0x8 等于 WS2812的 0
0 p! z0 A, x/ Q# U1 N4 w# c3 FSPI=0xE 等于  WS2812的 1/ B' s3 i8 l, c7 t) j' E
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte2 R; {; r) S; A' r
0 X4 x, Q1 m+ b3 `  W: u+ h

; b% p  f# J* tSPI速度及极性配置:
" T* b# E7 l+ u 捕获.PNG ' z2 a1 y' D( o6 V6 s

) f* D: X' S( a8 g( i, Z8 X& ?4 H& i/ o5 y( |% H3 a  f; K+ p
SPI只需要配置主机只发模式,这样可以节省一个IO口5 Q, s5 |( S( c; c
捕获2.PNG
  X* O0 z' H/ D; N  M9 e! z) Q- Q. N4 l0 s) r
驱动整盘WS2812都很轻松5 F6 f1 }/ ~5 \- B
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg 9 D2 V: F  F, h, V/ y

0 r' N7 ]+ h/ ]* {. n4 [1 G% c- k1 e驱动库说明:
! O  i. _$ D" U" l5 E2 H2 k; X* x例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程0 B# a5 t" d3 l% q8 X
程序移植了Adafruit NeoPixel库函数,, y% J! h9 ]) l0 w: `
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
: N! g: `# J3 _只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。. w; V+ W+ U% E9 ^7 C" ^
捕获3.PNG
0 @/ {- m& v5 U; R4 {/ U, z2 c) L0 Y7 I

/ h: W0 `1 h+ p! _- b- V

SPI_DMA_WS281X.rar

下载

584.49 KB, 下载次数: 1161

驱动库

评分

参与人数 4 ST金币 +16 收起 理由
yogolu + 5 赞一个!
hacker + 2 赞一个!
g921002 + 4 很给力!
Inc_brza + 5 赞一个!

查看全部评分

1 收藏 28 评论77 发布时间:2018-2-6 15:03

举报

77个回答
jjbboox 回答时间:2018-4-10 13:34:21
本帖最后由 jjbboox 于 2018-4-10 13:39 编辑 1 q9 [6 P& \& h# K. m. t
翱翔云端的鸟 发表于 2018-4-10 09:45; u5 ?4 A) }2 s0 ~* I2 ~
选择数据当做0码 1码的时候  是不是需要 首尾有限制

- S  F# G, @* z3 {# V3 V用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
9 f4 d/ B. j3 y3 U, {/ o$ \9 z9 n, U
以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。6 ]; j0 _6 N/ r9 ^2 g/ J8 J, M8 S
. B9 _' D% n  p1 ^  i8 L
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010$ ?% e+ s$ }: {$ M
  2.         #define        PIXEL_BYTE_1        (0x49)                                //010010015 `$ x" i% k- k
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    2 k" ~3 h% y& Q% n7 `* H6 A

  4. 3 N2 y9 _: ?9 D! {( s
  5.         uint16_t ws2812_spi::setbuffer(){7 `& K* r+ Y% ]  M; t/ ~
  6.                 uint8_t *ptr = buf_ptr + 16;8 m& y0 c) a0 c, a/ d
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    6 n# y: G2 G1 B: J' Q
  8.                         return 0;% G# V, b+ [# _+ _% ]4 j
  9.                 else {
    2 v$ f! a( }) }. L/ t
  10.                         for(int i = 0; i < pixel_count; i ++){& c6 `# f$ y* C+ X5 j
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    " K" W* ]4 o& L% E  g7 G
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    ) N* Z* v7 M% M) y- i+ C$ K
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);- x1 V- H5 s$ b; h9 h9 N
  14.                         }
    0 H6 g: L0 E* y! J8 U0 G) U
  15.                 }3 Y# l/ a1 w8 v
  16.                 return (ptr - buf_ptr);# d" P' N) B: u' P- f3 p9 E
  17.         }7 _, a" a3 L4 K. v5 A/ S) K( f
  18. ) d7 R& S' N! ?" S; A" ]
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)
    ' f/ b) u! w9 t5 A8 F0 l# s7 u
  20.         {
    % A9 a. p. H- W% k
  21.                 ptr[0] = PIXEL_BYTE_0;0 `, [, z; c0 m  j
  22.                 ptr[1] = PIXEL_BYTE_1;
    7 ?( ?( |/ T% E$ w7 E
  23.                 ptr[2] = PIXEL_BYTE_2;1 \6 M2 `& i- @6 g# e* W8 m

  24. 7 l4 W, u- r' U3 K0 v
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    ( ^" ], A( B1 H" _7 ?2 e0 N
  26.                 value <<= 1;7 P5 h1 s( k* l4 h
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    ( z! j# _9 }8 k
  28.                 value <<= 1;
    - j3 j9 U' n2 x; P# x& ~/ t
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    % D, ?8 I5 x  k/ K
  30.                 value <<= 1;
    ) E3 h$ p" G9 z$ I- v: L
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;1 t& K& t0 Y7 a" @8 \
  32.                 value <<= 1;
    ) b9 F: \0 v1 s) T3 i# V
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;6 p+ T8 [( Y6 z# h5 ], j$ g$ b3 c2 T
  34.                 value <<= 1;
    6 P/ F  C  P; i/ E
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;% V* A) F  F: C4 T
  36.                 value <<= 1;
    4 k6 a5 v8 {) ~
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;8 f; e4 Z4 J$ w% b; g* F" @
  38.                 value <<= 1;  ^+ S' V% q0 J5 p$ ^
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;
    : ~) h4 J  `( e* k
  40.                 value <<= 1;' h7 K$ M3 m8 z/ c: }# j

  41. * K4 N4 q! ?. c, g$ r- M/ Z$ p4 k
  42.                 return (ptr + 3);
    - w  B2 o5 ~) M2 ^
  43.         }% ^- n. t8 b. ?! G. Q. n3 `
复制代码
$ b/ l  {! D4 O6 n3 w' K, O" i7 [' M

; G+ \9 h8 u% q- J5 j. ?/ Q3 z. {- |
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?8 P/ F4 @1 @" S5 C0 g* V
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
: V1 V0 J, I: }) p4 B4 S- q{
3 V/ w- x* n) y' {4 h) j3 Z, v- a    uint8_t newBrightness = b + 1;
4 L0 S* I) C: M, D2 P    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value9 I- X" C* q' O8 F$ O; [
    uint32_t c;
/ S' R% t$ Z9 r2 r' ~    uint16_t scale,i;
% N% D( }4 ]* [    if(newBrightness != swObj->Brightness) # N8 @: G+ L8 x3 Y, F  Z+ g" F3 T  J
      {    // Compare against prior value, ~; N1 W& M* {8 r: P
            // Brightness has changed -- re-scale existing data in RAM" \! N( D; j9 n- i" s. L/ n
9 |: M3 o% M* c- p- i5 d
         if(oldBrightness == 0) # y/ K+ _6 H9 d
                scale = 0; // Avoid /0" t; R8 o/ e5 q
         else if(b == 255) ( H% W' U! `7 X# q; h
                scale = 65535 / oldBrightness;
  u# e  L1 d/ M         else
) N& o$ h% A" X" i4 p4 S                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
# i" i; K# i; S/ T' I5 {8 c8 j4 G: C' g
        for(i=0; i<swObj->PixelLen; i++)
9 G# R* S& Z* g* W4 b; B5 ?  v         {
+ V* }; ]0 l: b" i7 p7 H            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
0 D" S1 W$ [2 o% V9 @: A' W0 v/ d4 T8 U            c = Sw28_ScaleColor(scale, c);: {( V: b: b' L' C* }: W. D
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
- \  V& R. l  s         }0 T" Q5 ^4 o3 z
        swObj->Brightness = newBrightness;, G/ O: k' P2 J
    }9 K7 `6 }5 z9 u* i3 d
}
# N9 M& P2 _; B; `. C3 ]
9 d3 M- W  i3 j  T2 n% x: Q2 ^) I" {5 x' A
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
# e5 B  [' X5 v5 q+ L- C' R" M9 L是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
1 H4 Q' k# s2 V5 x  _: I& Y0 B: N比如8颗粒的灯条,那么分配的内存就应该是 ...

5 |0 F+ l8 r- _我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     % G7 V* L- ]6 g- F% Q9 @
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色  Z4 P; a# S: g: A2 K+ {* _
按照G R B  传输 为   0x00  0xFF  0x00   
/ R: o+ z; a, A7 {1 ?* u  I. K$ q* B& ]% J$ R; ^; ?+ z
转换为二进制( P* M! u7 T% S* [
001 001 001 001 001 001 001 001 001 001 001
6 u8 }# @1 E: t7 @3 H2 d110 110 110 110 110 110 110 110 110 110 110
$ f  ^0 x& i8 m$ J' m001 001 001 001 001 001 001 001 001 001 001 3 q/ @7 d" g' ^9 K/ Q6 u; J
- ?: A3 J* O8 G4 _9 g( C1 u
SPI数据为:1 p7 z& w  |4 H5 v
        0x24                0x92                0x49. W8 u1 S/ N. G' u# h( `
0010 0100   1001 0010    0100 1001  1 B) ?  Z/ `9 H( i+ O( C
        0xDB                0x6D                0xB6# q- P: c' Y0 z% T: \( O( n" ?
1101 1011   0110 1101    1011 0110
1 @/ v* ^- O. v6 A  i+ w        0x24                0x92                0x49
- N: e' V* E$ [6 T- E: i0010 0100   1001 0010    0100 1001
6 Z9 e7 n# S) T9 r, a
7 r6 R2 z/ J' F. U5 j2 o/ j4 F
% H- w0 b8 `. C这里的问题是当我SPI发送完0x24之后  发送0x92的时候  0x24的最后一位是0  0x92最后一位是1   导致0x24的最后一个0码和0x92的1码混合了  
七哥 回答时间:2018-2-6 15:43:14
这个思路很有想法,应该加精
waiman 回答时间:2018-2-6 17:56:19
楼上代码有个BUG,回去修改后再放上
waiman 回答时间:2018-2-6 21:28:57
修补改BUG后的代码:
! X( S) a) B0 B SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 897)
onev 回答时间:2018-2-8 11:17:30
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我一份?如果是文本编辑器看起来不像是vs code  更不是sublime text。
zero99 回答时间:2018-2-12 14:16:58
感谢分享,已汇总到2月技术原创  https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html
waiman 回答时间:2018-2-13 01:05:25
onev 发表于 2018-2-8 11:17
& W7 {' J" j9 }# r5 ^: ?我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
4 `6 n1 Y0 g7 b2 t, w
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。
5 F: F- D2 j) k( q( y好方法。
板子粉丝 回答时间:2018-2-28 09:36:53
先看看
jjbboox 回答时间:2018-3-1 09:35:48
很容易就实现了,非常方便,感谢楼主分享技术。
turingcode 回答时间:2018-3-5 00:36:15
学习学习啦啦啦啦
幻想卤蛋 回答时间:2018-3-7 22:11:47
谢谢楼主,学习下
xiaoxiao111 回答时间:2018-3-21 10:25:51
ertwhtrh
xujianhao 回答时间:2018-3-21 15:40:11
刚学,来看看

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版