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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
' B% I& ]: u& s  v1 p# o  x2 ]& r9 I; O
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
# d; O# K0 V4 w( G+ U5 h( F效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
9 F. c) k2 H& n! C0 X% x5 d5 A 2017-01-21 09_12_30的屏幕截图.png + t5 R" V! I* \6 c
1 n- F3 |7 B- |; r3 D
仔细一看发现 WS2812一个bit 周期是: * o, k% ?. V7 K3 H% W+ b3 C+ D
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
' M; `( U2 o4 A如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
: \9 o: s) C! tSPI=0x8 等于 WS2812的 0% t5 O* b) E* ~7 f5 w3 ~4 B
SPI=0xE 等于  WS2812的 1$ ?# `! o5 Z8 a
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte" j3 n3 p* e9 [; n& a

  l9 [6 a  l* W- \7 ~  S
5 j8 _- w; Y! B4 Y1 Z! Z2 NSPI速度及极性配置:2 w9 T8 y- H( F0 |/ H
捕获.PNG 6 j# l9 K" x5 l$ Q2 P+ Z5 m
) u! l9 l3 Q; ~0 j- y6 m/ v. Z

* n$ a- _% O9 I; X: \SPI只需要配置主机只发模式,这样可以节省一个IO口
; {# Q9 d# b& M 捕获2.PNG * q; b) }" S5 n7 Y3 ?' Q/ f7 L% P& Q
3 N% K1 C; C/ Y1 m7 B! X9 d
驱动整盘WS2812都很轻松' k$ I) M! c' Q5 O1 h. p* Y2 k; i
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
4 {6 h& F  C( ~, s. S( q7 L
, V1 I% V: H: N8 e! n, R驱动库说明:
% @8 y8 l; }9 ]8 _6 J5 n例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
2 [; [7 f9 [7 m: @程序移植了Adafruit NeoPixel库函数,; L) J) x  b& }; X5 i$ P
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
4 z7 Q/ N2 E+ a6 N  }只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
5 Z3 X# O" [! |$ N' {4 u 捕获3.PNG + m7 a9 N& H0 J) {* ~& E4 X# V5 d3 D

1 P" D, V4 k  H7 z% Q, D8 t  p2 p- l

SPI_DMA_WS281X.rar

下载

584.49 KB, 下载次数: 1200

驱动库

评分

参与人数 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 编辑 # ]; u' W$ P8 ~
翱翔云端的鸟 发表于 2018-4-10 09:450 K, I  Y5 m' z+ c  V$ p
选择数据当做0码 1码的时候  是不是需要 首尾有限制

, J! D1 J" Z- m, [3 w用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
4 `# r3 a, R9 ]! D- G5 k' H
9 K4 R3 n. B, t: }. C$ ?以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
- g2 p5 R- g7 B+ J% `4 |2 T* C3 x
1 j" A! N+ d5 P3 O4 U
  1. #define        PIXEL_BYTE_0        (0x92)                                //100100101 W( [) i% y* E" [4 h8 ~# ?2 F; j1 v
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    ) ~& J7 b$ @7 W. Y
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    1 z  U* D! _4 O8 J1 f, ~

  4. ; F0 t3 U! b$ e
  5.         uint16_t ws2812_spi::setbuffer(){
    ) i/ e, v! x( n8 Q
  6.                 uint8_t *ptr = buf_ptr + 16;
    " Z  k8 A0 p: ?
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    : a3 y0 l. L) q3 v
  8.                         return 0;$ A  G8 `7 G/ i/ @. E% ?! b: n- ]
  9.                 else {% Z1 [) a0 X2 L$ s/ u4 {$ F8 Z
  10.                         for(int i = 0; i < pixel_count; i ++){5 R& z) `* K8 @5 j5 K7 A5 K
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    $ _) ]- k1 O" T' P: B# \
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    + ?8 n/ J7 }/ N% F
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);
    ) _0 _9 }' Z0 a# I- j% b
  14.                         }3 |2 c% `% m$ T7 P
  15.                 }
    0 p$ t& ~& @% s
  16.                 return (ptr - buf_ptr);
    5 @( C' |# ]9 h. ^7 R
  17.         }/ P; e* C! k8 D" ~7 O  w+ k2 C" p# a
  18. ) a* V  |: W& o7 v/ b
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)
    / K, F6 e' @, T: U3 Y4 t
  20.         {3 V8 ?; o2 R0 ]. J
  21.                 ptr[0] = PIXEL_BYTE_0;4 B- Y9 U2 _* A7 m  E
  22.                 ptr[1] = PIXEL_BYTE_1;9 h1 K: [" P9 b& \: s! Z- Y1 E
  23.                 ptr[2] = PIXEL_BYTE_2;
    ' W! c  `' J( y6 K& T

  24. * ~( ~3 R0 P/ ]; ]) Y0 s# k
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;5 ^2 V+ u' Y0 g0 B* x/ [
  26.                 value <<= 1;) U4 r  ^, b* X; j4 _7 t0 D0 B
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    7 T# N* m, `3 V$ S9 O$ H1 j
  28.                 value <<= 1;
    . d/ D% H5 P) t4 N
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    + q# L# v8 V8 N! |% y
  30.                 value <<= 1;
    / E% S1 z$ R0 G# J
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;% I; F8 ~1 V6 b% G
  32.                 value <<= 1;$ ?# Z) I: F5 C, F6 K, c
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;
    % c- J! S: S1 ?( [, N1 J1 a3 N
  34.                 value <<= 1;1 g7 N& [; y1 s6 y$ V% j5 W/ r
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    + d1 h9 a! z* U& E0 K( Q9 p
  36.                 value <<= 1;
    ' p$ `. o+ d/ t4 j* G% v2 |
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;& q+ y; s3 p8 y4 u  ?1 q
  38.                 value <<= 1;
    0 |4 g& v  T8 D; O" s3 A0 s
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;% F; i, i9 n  K4 K+ D( }% C
  40.                 value <<= 1;
    & X. Z* Z. o2 p  g7 n" Z

  41. . V  M7 y3 G8 v' u9 i; g
  42.                 return (ptr + 3);0 G  z& n9 g- s( O
  43.         }9 \% y5 J5 _' l  L# k
复制代码

& b; S8 B" K8 Z0 k) b, ]( o5 L
  s$ I0 l( ~: u% P8 v
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?7 H  g2 A5 }, T+ f2 ~- P! A
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)  c# `+ x+ R) Y8 {, i" ^) [
{
1 r: R0 V! X' |/ O    uint8_t newBrightness = b + 1;
8 K' P3 {: J# m* H( L# F6 G    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value5 J  c4 c6 O( v3 Q
    uint32_t c;
7 v& p1 X3 z9 g$ D    uint16_t scale,i;
- m! a  {( R0 L  V. ^! G* M& F$ D    if(newBrightness != swObj->Brightness) 2 g! K$ q0 h$ t/ s! `6 d9 w0 i
      {    // Compare against prior value) _4 p; [/ M$ x; L7 V1 @2 w
            // Brightness has changed -- re-scale existing data in RAM
5 t% [2 e: p2 Z# t: |7 I3 {9 x9 s( @
( u( j, {( M7 ^+ M         if(oldBrightness == 0) . l" e: I. L+ g
                scale = 0; // Avoid /0
$ I* ]$ o  r( n3 @         else if(b == 255) , `3 h0 ]+ g2 L; ^2 C
                scale = 65535 / oldBrightness;& R' Z7 b8 e/ z/ r
         else
) F( L! @  }2 t- f4 c4 C                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;- s+ n7 j# U) J  ?/ e, ~; ~( Y/ X
) f9 B4 @  w8 [& `7 h
        for(i=0; i<swObj->PixelLen; i++)3 R' H6 [6 p1 c- W8 r0 k
         {+ H  p' e% k: w0 W
            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  2 j! x/ E/ f4 b
            c = Sw28_ScaleColor(scale, c);' i# K& O1 h  `
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色8 ~3 P  E: b* z8 [* a. B& E9 T4 y
         }6 Z+ l' [6 G) M; r5 ^! f
        swObj->Brightness = newBrightness;" E: _1 h/ F$ f0 |. n; n
    }# D/ e$ Q7 c* v! D  Z
}
; I$ q) j- i) V2 X4 N7 S7 A5 P  h: {% T: y9 v" R
: V- ^; K+ P# s6 t
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
0 Q  }, l; E' a是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
# z1 v( j: c* ^- p比如8颗粒的灯条,那么分配的内存就应该是 ...

' b4 o+ D' z2 H' V3 p$ L/ v我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
/ Y5 \  C: g& r; W6 u" x当我要发送9byte的一个24bit颜色值的时候  比如是传输红色2 I  F, a' s% I1 E
按照G R B  传输 为   0x00  0xFF  0x00   1 V  j8 ?0 D2 T: s0 |
& v  [. h* t; m4 x: S6 J9 `) Q
转换为二进制6 b% }7 Z# H3 o4 |, |0 F
001 001 001 001 001 001 001 001 001 001 001
! m. |, S- e; F$ i+ _' p/ o110 110 110 110 110 110 110 110 110 110 1108 K* q+ Y1 O9 ]5 Y& i
001 001 001 001 001 001 001 001 001 001 001
8 Q4 [8 T, D* u& k- X9 d( q7 f; z# A( Y# E% Q
SPI数据为:8 Q9 _4 c5 P6 s+ l( ^% P- y. e$ ?
        0x24                0x92                0x49
. ~( u# R5 o# k' T" F4 k0010 0100   1001 0010    0100 1001  / s6 t4 r2 W- G+ |% j, {) |
        0xDB                0x6D                0xB6
+ |$ }' z  ]8 i" e# W4 i; O1101 1011   0110 1101    1011 0110" ]0 o3 Z/ L1 R. r
        0x24                0x92                0x492 I" M2 S% `! C8 ~0 m8 F9 O
0010 0100   1001 0010    0100 1001
: E+ V8 Q  V5 O5 x  |- c  H: ~
5 e3 v& o$ \3 c; L8 W
" X& S. ~( F! f9 W, K这里的问题是当我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后的代码:# Z6 W+ u# }: y
SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 913)
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
- g! `7 P9 A) Y3 f4 t/ c我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
; F# E# e8 t# b. R# e2 L' d
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。4 _" W# A6 G$ K- ^% w; j  T
好方法。
板子粉丝 回答时间: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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版