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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
3 b4 B. E* u5 X4 V; v
3 G, a0 n" O1 d看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html9 Q0 `, F  z6 D
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
+ t( D  u, F  _/ O" d/ Q; h1 q* l9 C 2017-01-21 09_12_30的屏幕截图.png
0 l& m& F" }& \+ c8 k( ?
- y+ B9 R/ G+ @" B+ o8 \. K仔细一看发现 WS2812一个bit 周期是: " h; K& A7 Z0 B$ N, V& F  K$ z7 `
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
( g" K' @- T; V7 m. m1 D4 l如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。2 b# d' U1 Z% C7 W
SPI=0x8 等于 WS2812的 0
3 V" q. F! x7 Q$ {  Q. v" NSPI=0xE 等于  WS2812的 1
" {& v. f( k8 a也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte& {* i& r! p7 @0 J) ~$ e& D

# G* d* Z* K8 y9 O8 o$ ^9 m' c0 F: ^% c" u
SPI速度及极性配置:
8 g0 l$ Q( o; a7 Q* }% F 捕获.PNG
9 |  \( p! H# Q, A" P8 p
- j" O5 [5 n' H4 t5 b
7 _% H) J8 F# d, V$ Q2 W9 c0 JSPI只需要配置主机只发模式,这样可以节省一个IO口
) ?3 E; f/ r  A3 Q3 q 捕获2.PNG 8 V/ _/ m3 x' C6 {

9 f3 G; l/ a0 w! \驱动整盘WS2812都很轻松
3 f# m' w+ ]0 x) D5 \ 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
+ G1 G/ R+ K& y1 u) ~. h- C- m" Y* p. N. d* b2 W7 C0 }6 |
驱动库说明:) o- Z. ]1 F! w/ B( H* n
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
; [/ P( P2 Z9 L' j2 S' Z程序移植了Adafruit NeoPixel库函数,1 [1 @: J8 V6 G; e: y# n. Q$ X
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,; C; j6 e4 \8 X0 u' Q
只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。  o# I" n+ U/ V5 }. ]; [% |. F; F
捕获3.PNG 5 A9 n: k  M5 {
, P2 o, Z/ r6 K" S
. z1 ?' p' n0 }+ N* e/ [- ~- {% k

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 U; I8 D3 [: z) @' H
翱翔云端的鸟 发表于 2018-4-10 09:45
( V! h2 ^. H9 h( C4 w4 r. x3 a5 {$ u1 d选择数据当做0码 1码的时候  是不是需要 首尾有限制

" u9 s. @* H2 Z% t9 g, Q3 A$ e用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
! D$ y# s1 N" o
; b0 H' o/ B; m& s, j7 D6 u以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
. g0 K4 v" X" b
# Q* i# h% K( A! Q
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010* {5 y' c% P7 i$ X5 e- P3 [
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    6 }5 l7 v& w" Y; B5 {" e
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100& w- f# O6 F1 z1 |7 A

  4. 0 w& i" R8 n9 D7 O  ?
  5.         uint16_t ws2812_spi::setbuffer(){$ E0 ?8 ?2 j$ Z, p% a  h
  6.                 uint8_t *ptr = buf_ptr + 16;) E  F6 g$ g1 q- B. x5 ~3 Y$ Z
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    4 S* W8 D. m' n" r
  8.                         return 0;9 H4 G+ S/ Q$ b; `9 O2 j. D* k
  9.                 else {" g0 t6 N( B2 e% \
  10.                         for(int i = 0; i < pixel_count; i ++){
    ' M, H& J2 L4 R4 L, s
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    % V! f4 U8 ]% L# E$ F% M2 ]" v
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    ( R/ F# D5 y. W& \9 ]
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);% X+ r; I; d" P
  14.                         }
    . [. t0 ^' V' r$ G3 v
  15.                 }& J; X( A' B- {" o8 ]3 I( m
  16.                 return (ptr - buf_ptr);& z2 k0 h# R' t& r, c
  17.         }( O, b/ A+ [6 _/ q: M
  18. ' ^  v& T7 I8 W- }/ n6 z
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)* M9 ^; `. O2 |6 d
  20.         {
    5 v* H1 H# J" t
  21.                 ptr[0] = PIXEL_BYTE_0;5 H* `3 s6 N" K4 a, v
  22.                 ptr[1] = PIXEL_BYTE_1;
    2 R& d9 B3 x" H, S/ O; N3 T& o7 l8 R
  23.                 ptr[2] = PIXEL_BYTE_2;
    ) r/ L2 E0 w) w; t. t9 [' n

  24. 0 E7 J! w2 M: b
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;
    % ?7 E" A4 w. ]- U  E
  26.                 value <<= 1;
    % v! x5 E8 E) \9 U, D
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    - Z. E% H+ D2 E& V) r6 N% l, S
  28.                 value <<= 1;
    + m9 }) j1 V0 W" p  z/ o$ c7 t
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;& W  R4 U1 |2 |% Q9 u2 t
  30.                 value <<= 1;3 F, t) u- R; C2 M, ~
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;; ]& F  l6 v5 K+ E/ g2 |
  32.                 value <<= 1;2 {7 A/ W. Z- N- R( g
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;
    - v0 o3 |$ z3 F, l9 g# Z
  34.                 value <<= 1;
    3 Y" K6 U5 i4 p
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    " c8 K% p4 U4 O3 a
  36.                 value <<= 1;
    ; R0 B4 O5 o: z% C: Y8 A/ W1 q
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;% j! q$ B" t+ }+ V! I& E
  38.                 value <<= 1;6 d4 v3 a; \) _0 e* ^. Z2 K  R$ N
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;( e& V" A4 D; {# u9 j
  40.                 value <<= 1;
    $ m# d! L6 E" {, N) \) w# c

  41. / J9 X" {& |5 M+ P  R
  42.                 return (ptr + 3);
    : b- @; m$ @, D) T3 r, j
  43.         }2 ?6 ^  |4 j- K2 @
复制代码
; N4 k! B5 ^7 h, |6 P
7 H2 i+ W: X: ~+ k! ]
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?. \- r! q6 ^! ^- o& f" s
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)# K; T% V/ L+ Z0 {4 R6 @1 Y/ [
{9 v% c4 x( V$ q; e) L' I* H6 ]
    uint8_t newBrightness = b + 1;
8 ?2 G- V) \! w, b$ u' y/ w7 d7 B    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value" U8 T* Y5 M3 B; ?* j5 Y
    uint32_t c;
2 O' F7 E, W5 G    uint16_t scale,i;
& f! ^. a# k! S3 Q* }% l, @    if(newBrightness != swObj->Brightness) # L, q9 x  V$ Z) W1 W1 S
      {    // Compare against prior value
7 s0 ~) G8 q' A/ {' @* Y$ t            // Brightness has changed -- re-scale existing data in RAM
) L0 U& q7 D1 V5 W" V) Q% M* N+ f0 h' M$ y3 |( z  D
         if(oldBrightness == 0) + f# x7 e$ g3 k% v
                scale = 0; // Avoid /0
" u0 n/ B9 `- C         else if(b == 255)
- A$ k9 g* S2 m. D! I                scale = 65535 / oldBrightness;
. i  m) q% j7 m0 i# u  U# _9 F         else 9 {% r4 P2 k0 l* ?, i: H
                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
) H& x; W$ A1 Z8 Q& n. }$ `1 [" n0 q- R1 ^
        for(i=0; i<swObj->PixelLen; i++)6 }( s" O6 O- v- X* G
         {
1 X1 _. W4 {0 T; R! s& [9 M            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  + L  j7 r' H, f- v7 N' _! ^
            c = Sw28_ScaleColor(scale, c);7 `% R& D7 H$ K
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色/ \7 @0 n3 g% \5 {1 s" [
         }
" g# _' n- X& }/ z9 {; _" w7 r+ n        swObj->Brightness = newBrightness;/ d. o5 R% H  b- A4 n! b6 P  O' a
    }
0 J2 s) p4 P* q5 V; i+ [}
/ X) R+ r# E! a2 U! Y$ t+ y: Q1 x$ k: \' F4 s9 A
2 b0 i: C, l  `5 @  A
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:302 v/ T6 d# f9 }1 x; U& o
是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
" T; A2 M0 P! Y* w: _/ f! _比如8颗粒的灯条,那么分配的内存就应该是 ...
+ m  \( T# ?: d3 G4 G( ]) \* _( R6 ]
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
4 N* Y% k% s: ~& f! {: x7 @5 k当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
* Y4 l& \0 z3 q1 ?) q" D4 E. _按照G R B  传输 为   0x00  0xFF  0x00   $ @2 ~: ?$ U4 u
  L/ a* T5 Q. X; B
转换为二进制9 t9 h% h4 F9 S: Y4 G. d2 x$ V
001 001 001 001 001 001 001 001 001 001 0019 M2 E6 F6 N" I% Q. Z
110 110 110 110 110 110 110 110 110 110 1106 @0 k" A7 n: U% P" l1 t
001 001 001 001 001 001 001 001 001 001 001 : x- a) a4 ]9 F! O$ `) y

: c8 x' d7 H. L: V9 u) K' USPI数据为:# b7 X) w$ J# B0 w8 O, Y
        0x24                0x92                0x49
+ p$ E0 x/ c% b0 @8 w, e& K0010 0100   1001 0010    0100 1001  
! a$ F4 k( }% O$ }0 _        0xDB                0x6D                0xB6
8 K+ u$ G6 }6 @  l  M6 L+ ^1101 1011   0110 1101    1011 0110) v' R0 W; V, U% I7 r9 S2 I8 t
        0x24                0x92                0x49
( X" n6 t- y, Y! R0010 0100   1001 0010    0100 1001
/ E0 u/ ^. E$ }  F( l5 b! B3 c
( ~/ b3 W, k# r) X( E: C* ]) C# I) P+ I! p+ V
这里的问题是当我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后的代码:
. p  t3 O& |. \8 O- c8 @ 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
/ B, {( N4 c: K4 M. D, L. j我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
* p: X3 Q! i, _
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。
6 u$ C' [6 H! q# L好方法。
板子粉丝 回答时间: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 手机版