请选择 进入手机版 | 继续访问电脑版

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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑 , _- z; A. Z! a

7 @& v% v& Z: T: b9 ~- Y看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html3 \& l) }( c4 [" i. g
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。/ _5 l2 ?6 @( ?( t) F9 L
2017-01-21 09_12_30的屏幕截图.png
6 L0 `; [% ~+ |" t1 {# c) T8 G+ q4 t! Z3 Q1 j0 u, t
仔细一看发现 WS2812一个bit 周期是: 8 w4 w+ M3 a1 T7 G) E9 O+ P7 C
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns, u0 x7 A: ]% Y8 C
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。/ P6 F1 `; H9 y6 m6 Z
SPI=0x8 等于 WS2812的 0! x' \' g4 y) }: _5 \( ~8 ~( E
SPI=0xE 等于  WS2812的 1
0 R% B& a+ T0 B$ l: s% E也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte6 m5 B4 R: v8 I
6 L5 A4 n: ]' w: ?# V* l9 y

, s$ t6 `3 [5 U8 MSPI速度及极性配置:
' b  T! X# N. G  c# T8 p( G/ c7 g 捕获.PNG $ z' q* ]2 m/ ]. `* @& x3 w
, _7 s3 q4 A5 ~! i! N3 v5 c3 b
7 F: N5 S: V- @+ |
SPI只需要配置主机只发模式,这样可以节省一个IO口0 j+ S) D4 A, v- S
捕获2.PNG
. D3 d; u7 y1 `- V* T  n, r5 }
驱动整盘WS2812都很轻松% P6 `/ ~9 h3 v, h1 H# u
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
+ g& N& ~, i# {; T: u' k7 w' K
9 e7 T8 {# }! S  L) k. o/ a0 C驱动库说明:
. v% Z7 t6 h% F/ m例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程# n1 h3 E+ B1 J% @# s# E4 n! p
程序移植了Adafruit NeoPixel库函数,6 V* p  ^' d( E7 L; U, |2 c2 R/ N
采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
) r8 [: }! ]3 X. f" ]只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。4 ^$ K+ P2 \0 ~( D  J$ \' a
捕获3.PNG
) l! W" z5 A6 _3 P: q6 m0 y4 a, M1 g5 V8 A

4 S  Q: q7 u1 O5 r* y) H

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 编辑 : p! i/ U" X. ?# z4 N# p% v
翱翔云端的鸟 发表于 2018-4-10 09:45( n7 g! |- x: n" V) g, ~: U) w
选择数据当做0码 1码的时候  是不是需要 首尾有限制
6 m& U1 |" D0 n/ ?/ M- Y2 d
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
- r; c1 O6 u9 C7 ^. z! a8 k; Q- U5 A# |' c) K3 J
以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
5 C! T6 ]: L; i! d- ]3 ]) w) `
# u- t( g' _$ D3 h) g  A
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010$ v0 H) y! f) a/ }  @; o1 q) r$ q
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    ! R3 d+ o$ s- t+ A: \' \
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    & j3 F9 Y  N$ `+ x( h

  4. + F$ V. F7 R) ]- {; [9 h
  5.         uint16_t ws2812_spi::setbuffer(){9 q+ ?: M( R3 z* B) V- @  y
  6.                 uint8_t *ptr = buf_ptr + 16;: @$ h2 t$ @& W9 C. x, D
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
      ^; g9 ?0 l6 `+ |. M5 a0 k
  8.                         return 0;
    / J1 L5 a1 M7 r* M, {& B9 t$ q7 E
  9.                 else {" J7 N1 c. e/ N( }
  10.                         for(int i = 0; i < pixel_count; i ++){
    2 b3 ~2 p- O5 S" H1 J: b  G8 R: [
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);* P1 P% ]. R9 S6 B3 u
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);' x4 T( {0 }( I/ v( k
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);; L1 Z3 k% n5 Y: T5 A/ h4 V
  14.                         }
    % w, C4 a& v. B, e# d
  15.                 }
    ' d6 X% _' S, l9 O* V& i8 W
  16.                 return (ptr - buf_ptr);
    0 g* q0 J$ W* d2 _9 l* a8 P; T
  17.         }
    8 y+ [# o/ l/ ^3 x6 Q
  18. 5 Y+ n; Z2 s! k. p* Z
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value), k( u$ }+ p7 B! s' P. U: [! G& }
  20.         {
    / t# v1 l  @  h- P
  21.                 ptr[0] = PIXEL_BYTE_0;
    6 i8 c/ q: ]4 u8 u) E# e
  22.                 ptr[1] = PIXEL_BYTE_1;
    3 n* P, K4 O4 b1 |+ R3 u; v9 X
  23.                 ptr[2] = PIXEL_BYTE_2;9 i# h8 m) z# c) j( J
  24. 4 m- \7 M4 l& C0 m0 x$ p
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;  ~% v6 G# r+ G0 x0 D$ s1 x
  26.                 value <<= 1;0 I' B, b) q1 f$ E# ]9 J
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    , Z! r9 l6 H  Y( g2 k: z
  28.                 value <<= 1;; F( j9 o$ M/ k2 y4 F* C  n) `) f
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;1 F) ]5 `  p+ G9 M8 a% |) V8 S
  30.                 value <<= 1;
    - L. H5 E  [4 u; E
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    " B" G8 k( I3 S
  32.                 value <<= 1;8 Q" _+ Y% T. e0 U. P
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;& g5 d2 [( I' X  z, w& I- x
  34.                 value <<= 1;! U' R: r& a8 v8 Y; }$ |8 h/ M
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    9 G! o! V" p; I8 R; \
  36.                 value <<= 1;& W# k8 n0 F' I" J! f$ k
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;% v- W( P( l' Z9 G" S  A
  38.                 value <<= 1;
    , u4 B5 z, q+ f  q
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;$ Q2 ^9 s3 \% ~/ c
  40.                 value <<= 1;
    ' d; ^! L+ M2 k3 u( l5 p

  41. % F( b9 q- L, Q7 Z4 f8 N
  42.                 return (ptr + 3);
    , N. s3 X. u- e* h4 m
  43.         }  r( z, M; ], c" E0 G/ G/ X
复制代码
# K( V' u- R, b9 c% i
3 p) L) A+ S, B, v. ?) v) N
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?& ^! }$ i  }" g0 w7 o& i4 E0 R
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
: ?/ R, S5 j' C9 F# h. \{4 m5 Z; Y* s& i) _) }$ K! S  c% A
    uint8_t newBrightness = b + 1;
9 j  `9 m1 [) B4 z- u3 F  M9 O    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
+ p( \" @& E  A. _# J6 Y+ Z    uint32_t c;
1 P) @& |; S# q. W9 J6 Q    uint16_t scale,i;
! B& q4 k. e7 K+ {* ^4 O- V    if(newBrightness != swObj->Brightness) ( v! C; b" c- e( R1 [3 b
      {    // Compare against prior value9 v7 ~" G' E* c( {6 ]8 h
            // Brightness has changed -- re-scale existing data in RAM, W3 D5 w5 @% a& S) k0 I/ J

2 H4 j; o. R* q3 ~         if(oldBrightness == 0)
' c; P  h1 @3 G: F                scale = 0; // Avoid /0) k5 |) r  M5 F& n, P
         else if(b == 255)
) V) [9 M" n0 E- d; A                scale = 65535 / oldBrightness;
- m' g; u( A9 z         else 6 s- z/ X6 c% T  t; f
                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
* v' Y1 Z* }/ |- }0 g. e# |* V4 P  r* Z% {& x7 p4 v
        for(i=0; i<swObj->PixelLen; i++)
/ p$ X; N/ c2 a( m2 j         {
3 D) E9 ~+ `* ]" s3 o) x6 f            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
% q' j  f4 x, ^9 }* z6 [            c = Sw28_ScaleColor(scale, c);
" a( m8 ~% }+ |/ y& N! U            Sw28_SetPixelsColor(swObj,i,c);//设置颜色& |; v  J9 |  E/ ^
         }
( }& Q$ ]' I. B8 I! W' v        swObj->Brightness = newBrightness;
" E# m$ C8 {8 ]# S, y4 w    }
9 ?% z/ s% e: E. Q}5 z) `5 d. X% e
) o. w: b3 y/ d3 Q) [4 m
* k) j/ D- c6 y, z! ~; S7 t. [
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
* }. `' P! Y0 n3 m是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。0 p9 C  i2 y& w; v9 k8 Z
比如8颗粒的灯条,那么分配的内存就应该是 ...
, T/ C+ X$ N+ R, W
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     * _8 I, [5 P( q1 n1 Q, W7 C
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
/ R) _% ^+ E0 }; b; D+ V按照G R B  传输 为   0x00  0xFF  0x00   ! `" q6 c0 v3 @& n  T9 Q0 A0 D

+ u$ m  E) \9 t! H转换为二进制( m, u4 c2 }. ?4 J6 m; K8 L0 c
001 001 001 001 001 001 001 001 001 001 0013 O$ y' X5 [0 G% Y4 O0 n
110 110 110 110 110 110 110 110 110 110 110; d/ S" _0 S- y" u0 v
001 001 001 001 001 001 001 001 001 001 001
) c: ?8 e4 \# H
, @4 p5 g  M& H/ x7 O  w9 F; P# GSPI数据为:+ a$ ~* r, V! N. `7 Z3 o/ B5 |/ T
        0x24                0x92                0x492 a# V& E* G) [9 I( o  t$ Y
0010 0100   1001 0010    0100 1001  
& ^& `( H% b; G8 O" ?) U        0xDB                0x6D                0xB6
5 K3 T0 u6 t& {) [' d1101 1011   0110 1101    1011 0110. R( T4 [5 Y. p
        0x24                0x92                0x49, ?. Q3 U( D! t0 H& A) s/ P+ a
0010 0100   1001 0010    0100 1001 # V4 q$ j# A) s8 h3 Y
4 c/ J3 o9 K) e- N. i1 p
/ R/ B' ~- X9 t) |
这里的问题是当我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后的代码:
5 h% U' e- ?' P6 ?3 g! C SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 896)
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
1 [- i( Y# H1 j3 f1 v  i我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
* ]+ n! o* c5 n0 D! K8 K& O
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。% V2 p% e+ J( d5 R) G
好方法。
板子粉丝 回答时间: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 手机版