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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
. g7 n) T) i" L! G- b$ ^. W7 [2 R1 V
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
/ a% q: \2 J* A/ T0 O0 T4 ]效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
2 n& s0 j1 R& C 2017-01-21 09_12_30的屏幕截图.png 4 {: z, d, A9 N. R+ O2 U5 w2 K

6 S: s# R& H3 t- I) v仔细一看发现 WS2812一个bit 周期是:
# z1 ^9 s* B: i         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
' j6 i$ h" z6 b# ]3 b  H' l如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
/ p1 h+ E' e+ O. z  z: \( _# OSPI=0x8 等于 WS2812的 0* f8 u: n0 T6 K# H& h; @
SPI=0xE 等于  WS2812的 1# F* S" U+ L9 b+ V. w& ?
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte
) d- F& F6 f# v8 g3 k1 ?. G; h* ~# U' C( }- ^4 M/ i9 d% n) V5 R
4 s4 B, W2 c6 d5 I
SPI速度及极性配置:
% X7 h2 \: M7 T3 J5 K8 b0 N) B 捕获.PNG " v4 T4 N* K' a/ L
4 c; l) q  Q# X

  ]2 t, ~: w5 M2 ]7 m6 ~* ?SPI只需要配置主机只发模式,这样可以节省一个IO口
0 a! [, R1 b7 v  r2 m$ Y" j( { 捕获2.PNG 8 V9 z: d% K/ v6 H
5 B% w! o) G) c% S2 [
驱动整盘WS2812都很轻松9 c' ~! k' G0 [8 {- U- S1 G; x
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
/ E: r% R: K; F5 @% z" A$ K* p/ B3 R; G
; O; P5 o. l: s0 |. h$ ]驱动库说明:% ]; P6 F& e) t0 b, \
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
; f2 N3 C  I: R  E程序移植了Adafruit NeoPixel库函数,. `* {/ t4 Q7 d5 n9 O
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
; z% ~8 u6 s) H1 a5 B只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
, \4 @+ e8 S1 S7 V$ |" v 捕获3.PNG
$ q" z4 X7 |8 m2 y4 a5 \. x& g
+ O; C6 j3 _1 l, x( R# k8 s; R( \9 c/ a2 s1 i

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 编辑 / H, d; `: Q" c1 d% }
翱翔云端的鸟 发表于 2018-4-10 09:45
0 }, m- N1 ?% j; f% n/ b* G选择数据当做0码 1码的时候  是不是需要 首尾有限制
3 {- U) ]" ^/ q( |. q0 o
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps2 ?6 ?' G* ?- S- K

# \( M! {1 J6 I9 y8 P以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。( I1 J( n" Q* L% F# ]4 ?: y

- F9 Q, w% F# ~
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010( ^1 {& N2 n2 Z* ?( R3 {
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    % F0 D/ v# |% A* _9 i6 j: _" G% x
  3.         #define        PIXEL_BYTE_2        (0x24)                                //001001005 ]+ ?; Y% `8 O- x4 u7 l5 T

  4. ! A  C4 l& J( f5 W8 n
  5.         uint16_t ws2812_spi::setbuffer(){
    % W  ^: `7 }" R* H* T
  6.                 uint8_t *ptr = buf_ptr + 16;
    % x- ^, F4 s! [$ y9 [
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)# L3 V3 o+ h4 X0 N1 u$ _
  8.                         return 0;* f: v/ [5 M7 U6 T; M; k
  9.                 else {
    % h6 g3 U; W: a" ^
  10.                         for(int i = 0; i < pixel_count; i ++){
    7 w) j6 {" H7 p2 C9 t
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    - D- ]+ j2 o& i, S* r
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    - |" }8 @2 r( ~. f- Q
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);$ I4 B4 f8 z4 c0 r
  14.                         }; {: A# c3 i/ ?8 Z; H
  15.                 }( D) F% {7 a$ q" m$ N+ O' F
  16.                 return (ptr - buf_ptr);
    8 M! Z! b9 r' M& o. [6 Y8 z
  17.         }& }# F+ @& C4 ^) u- u4 a# W

  18. 1 X6 }: s& s+ v* g, K8 P
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)9 i6 e- o* w. H% N0 f& u' w* W1 k, }
  20.         {) @) \" g0 V9 G0 |, o* R! l# |
  21.                 ptr[0] = PIXEL_BYTE_0;
    ) y: Z& @5 m/ f
  22.                 ptr[1] = PIXEL_BYTE_1;9 m9 y  h9 e. C( t9 U, z, ?
  23.                 ptr[2] = PIXEL_BYTE_2;
    , h" a8 ], q% I. {1 a3 k" R
  24. 5 @! x) \" ~6 ]/ H4 |( U- R9 U
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;0 T! j$ k3 u2 n4 ?( H
  26.                 value <<= 1;, }( Z. E  ?6 ^8 D6 o- S; \
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    & O( u3 f5 B0 ?. Z1 j8 G5 e  K) e
  28.                 value <<= 1;( A/ T5 U$ N; G9 }; Z  r& t  m6 P/ ^
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;* O! p7 h: f' d
  30.                 value <<= 1;
    + t6 Q: `$ a5 {& T! ~. \! O- J
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    ; r% A# R& R; s
  32.                 value <<= 1;- `9 l1 G3 @7 x
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;3 a5 m) J+ O9 t4 g* T! G  O
  34.                 value <<= 1;7 `2 o, q" R; Y/ q0 b3 k7 |# Q
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    . n" L$ o1 k* ?( e& j
  36.                 value <<= 1;4 O+ q+ t7 e  I4 d( o$ L* V
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;8 y2 l# t5 g7 U. `8 D' U5 |1 f
  38.                 value <<= 1;
    & `! R4 }. v. k5 p4 F
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;! B( ~# f% [1 O* Y" t+ i: [
  40.                 value <<= 1;
    6 X$ L# z7 _# s/ X

  41. 0 P3 [% m3 g% F& _( y
  42.                 return (ptr + 3);+ E0 x, j$ p- f
  43.         }+ Y3 y1 b; F/ P/ X! A: D( {: k0 D
复制代码

' Z" u; o# \: y( F+ P* [  s3 ]
' e* q# U+ g( ], D# l. u% L8 o  p
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?0 x. v' {4 y, |$ b# T4 @) m3 C8 O
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)4 s8 [5 A) \/ n& Q! k. c9 ~
{
1 ?( f: x: F7 x7 h    uint8_t newBrightness = b + 1;
, t. T1 e. I4 u. _    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value6 X8 Z) X' F( R* h0 Z
    uint32_t c;5 i+ z3 u! U$ s* |% q+ [
    uint16_t scale,i;
5 n% @- @' a! ]  p3 i    if(newBrightness != swObj->Brightness) 6 M. h3 a0 g* I% Y
      {    // Compare against prior value# ?* k- s+ _0 z" ~$ Y
            // Brightness has changed -- re-scale existing data in RAM
5 B) m* {1 S% W# H- g# z$ a
1 x% O  |7 }& i$ Z         if(oldBrightness == 0)
" {9 p; @$ ~' _# J  n: [, S                scale = 0; // Avoid /0  L& Q4 l- c; Q2 m( R" j% u1 Q
         else if(b == 255)
( S4 H7 r6 N8 K  ?3 Z0 _                scale = 65535 / oldBrightness;
! W* R$ @: v! f! N; Y% y         else
( D" L" k3 k8 H% \% O                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
1 x6 Y# R! ^/ _
% _1 Z: D& s* p: B        for(i=0; i<swObj->PixelLen; i++)7 ^6 W6 U4 m; x& J
         {6 _; f1 C- p+ \" R0 j
            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
; h8 U) W: n2 m6 C            c = Sw28_ScaleColor(scale, c);
* D; K* i1 C" X9 ]            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
" S% E9 k' ^( n/ J5 `" C         }
# ~$ d, d. M+ j$ E3 n        swObj->Brightness = newBrightness;% A8 W  v  u. u7 Q' \; V
    }
' K' e% W0 q1 |2 u- o# m}5 ^6 L) h  V+ L- R5 G- M
: M" c# e0 t  h8 h

9 ]1 N* B1 t% H, t3 c+ ?' s8 C
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30% h! V& x8 O: e
是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
" u% I! r+ ^1 j& Z, C4 v  S比如8颗粒的灯条,那么分配的内存就应该是 ...
2 ]- X, F6 o; @# T& t% l5 M
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     4 `5 d; [* a5 z- `. ^
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
; |6 o- e' w$ `1 z4 a7 D5 l按照G R B  传输 为   0x00  0xFF  0x00   
  Q5 L% |8 ?6 _) v$ K) |" q7 a' ^& {) }. E
转换为二进制: ?; v: v9 i$ H# }. l8 |
001 001 001 001 001 001 001 001 001 001 001
" h3 X, b0 f' z110 110 110 110 110 110 110 110 110 110 110
* v' W. a! F% u. x001 001 001 001 001 001 001 001 001 001 001
  }3 `  `! E8 J* `# s2 Z; ^0 k8 X
0 ]0 R# z9 @" R6 g% F$ _% ]' I& DSPI数据为:% q, i/ \9 d6 s) M0 a
        0x24                0x92                0x49
' a2 |) v4 [  C3 B5 E6 ^' ^( q9 D; d0010 0100   1001 0010    0100 1001  
% h+ }+ r3 h# _  [) T$ `: l        0xDB                0x6D                0xB60 g) E* W2 x  M8 V/ s
1101 1011   0110 1101    1011 0110
* B2 |( ~; V) y: A9 `* @        0x24                0x92                0x49! a( t9 C! Y( u) \
0010 0100   1001 0010    0100 1001
* m6 ^$ L1 V* B8 U" C# ]4 e3 ^, E8 ?7 z9 Y2 o6 S
$ s# _/ C% Z4 D3 `7 F
这里的问题是当我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后的代码:
3 R6 s) C- K4 B# T 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:175 k/ ^( L  Z) E# S8 O5 c% W
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...

+ [; j! `1 _( Z1 F/ w! p6 C# p  vmdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。3 |$ A+ N2 s0 c$ K$ y4 P
好方法。
板子粉丝 回答时间: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 手机版