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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
8 N/ s! m( C0 |* ?- s" A3 S* G
9 F% I; G8 g& @9 Z) ?# V看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
8 O# a4 D9 R4 K7 Q% u8 c5 R效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
. W! x. {% p- M% [( O* d" ] 2017-01-21 09_12_30的屏幕截图.png
5 e. @3 ?/ l5 ~8 i
, [. d- w- ~: @, Q8 l  d! z$ u8 y仔细一看发现 WS2812一个bit 周期是: 3 \; N) U& d& p+ I  t$ o/ T3 g# r  d
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns+ O  [7 s) H8 ^! r( x0 _$ [+ r
如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
: ]  F4 z# R. S# LSPI=0x8 等于 WS2812的 0
4 i3 a: _' N2 i% ~SPI=0xE 等于  WS2812的 13 N0 X0 P! K0 f2 p* i; g' O' K
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte, s& |; `. B$ T) M/ L$ E; G( X
% z( o( a% }8 k- p3 p2 R
4 R2 d& Y/ P2 G
SPI速度及极性配置:( Z) m' w. b2 i/ ]# t4 \) `" Z: B% `
捕获.PNG
! d) J" ~2 f( O9 p# z# m1 o9 \
& e2 S9 y- c9 g8 J8 u+ J5 n. }* h1 R2 v( O) E
SPI只需要配置主机只发模式,这样可以节省一个IO口
$ _" s! o" R1 l# U0 [- r3 m* I 捕获2.PNG - i& j+ p0 |3 o. b4 Q" Y6 \' G% m

6 x3 S! D+ [$ R/ e9 q驱动整盘WS2812都很轻松
/ {9 h( _5 h$ h& p5 r 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg / p6 W' t3 @5 W( T$ V
( }$ x$ v5 z% [5 N2 ]
驱动库说明:
: }# B; G9 @4 n; F6 k2 N例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
+ G: C4 v) V; s8 G( D/ o* {% F% K程序移植了Adafruit NeoPixel库函数,
6 ~  ~1 N8 M/ c/ e& i采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
: y- F5 q1 g: x, E只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
( U* @0 D1 L0 r* {/ ?) D 捕获3.PNG % R1 ?* e! }: N

) p  ~; h. ~1 o+ o2 r! O" t1 n
5 d5 A  \8 A" p& Q

SPI_DMA_WS281X.rar

下载

584.49 KB, 下载次数: 1202

驱动库

评分

参与人数 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 编辑
. I7 Z5 `' w+ F' B- q
翱翔云端的鸟 发表于 2018-4-10 09:45
" F# I" ]1 _: C选择数据当做0码 1码的时候  是不是需要 首尾有限制

9 P$ E; @. j9 B5 H用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
: i* S5 H! q4 C! }0 g5 Z6 J6 e$ `! U; j) c1 v5 g
以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。
  R: i  u/ }/ a- I, C! v! q) Y9 s/ b$ _
0 F3 a$ N8 y4 k8 b' k2 q2 W. \  g
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010. Y+ Z  {$ A# B$ x; U& g( b6 L& [
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    . N2 u* k, g- M0 R9 Y& w
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    / x6 D! u- B* ?
  4. $ p, v5 R# C, p. ^3 R& y- C
  5.         uint16_t ws2812_spi::setbuffer(){
    ; K  Y, k9 b3 S) x6 c/ H! ]
  6.                 uint8_t *ptr = buf_ptr + 16;
    0 v2 W6 l: o3 n' F
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)1 x# {5 o( O! i; I- P4 }/ X
  8.                         return 0;( P- c2 @( b, k; ?( ]
  9.                 else {
    1 ?+ h5 G  n" y. Q
  10.                         for(int i = 0; i < pixel_count; i ++){
    7 h" G& J9 Q0 E" P  Z) C
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);
    + U, p! e. e1 S7 h% W* b' s
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    . K$ u3 G4 I( I5 l
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);
    $ B, b4 ~! `5 m4 O) v& W
  14.                         }
    0 y, e" p. t- X4 y! W- y, p
  15.                 }8 {2 H; {6 R0 `) N" l! T
  16.                 return (ptr - buf_ptr);
    4 u; ]7 q! |" o2 J" _
  17.         }
    & |) Q, W& k0 ~5 v

  18. & r! i: z" h2 F7 `0 x
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)2 k* w/ `7 l4 r# p) w, `; k9 H* n1 o
  20.         {
    . {: l* m; }, T( y, G/ K7 S
  21.                 ptr[0] = PIXEL_BYTE_0;
    ; q" K- \& p+ Y; [) e+ B, y
  22.                 ptr[1] = PIXEL_BYTE_1;
    8 m- E% t$ |1 J3 W" K2 G' B
  23.                 ptr[2] = PIXEL_BYTE_2;. b0 z# ~9 ]% r% ^, Z
  24. ( D+ h) P& X# b1 G4 Y
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;" p- ^$ P7 v9 {0 E! Z
  26.                 value <<= 1;7 k( c2 B5 X( a7 D' T; _
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    . |! J% z" @) o' t
  28.                 value <<= 1;
    2 R* i5 Q( A+ E# Q0 i2 ^
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    ( O  x/ @- E8 @4 A3 H
  30.                 value <<= 1;
    8 j+ N1 p, p+ O- v
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    % X: F# B& r; \" y
  32.                 value <<= 1;* T: X8 o% \  @$ R( w3 k
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;, U: P6 K% U$ w$ r7 L
  34.                 value <<= 1;
    + W* X6 |4 U1 H  }! f& d6 ~5 f
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;* s6 s: x" P0 A6 d  D
  36.                 value <<= 1;
    % z( B% I, h( b) V
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;
    9 E6 m6 e/ `5 F, d# j
  38.                 value <<= 1;
    2 O8 T- Q5 p0 W+ c, ]7 G; D+ g- {0 E# _
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;9 _/ R; w& d  C) \# v
  40.                 value <<= 1;
    ' X$ D9 N/ M5 Z1 y. S7 Z

  41. % g0 N+ |% R6 G( R! F" G8 C) t
  42.                 return (ptr + 3);' e- N9 e7 a, |( f7 I/ q/ v/ E' d
  43.         }
    3 q. |' x; e* V) y; @
复制代码
& [% T0 s: L( H2 ?" [6 b3 p: J# o9 H' f
3 O* B) L' S6 R' d2 L& f* p
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?
3 l8 m) ~- K8 H8 L0 P, ^0 W; Xvoid Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)% O4 E( a0 q! L+ v7 b+ u
{
6 b2 a) s/ @# ]* n    uint8_t newBrightness = b + 1;# q" L0 h8 x1 {# a1 U" T2 A: H
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
) R8 U: h: A! ]$ q8 ?    uint32_t c;
' A% h$ @8 o. k& u) k! S( t    uint16_t scale,i;
$ e9 Q5 G: M9 i! z    if(newBrightness != swObj->Brightness)
5 @+ p+ t9 V5 ?$ l6 F8 X' e% o# h      {    // Compare against prior value
3 }+ n0 I+ l( N2 S, E            // Brightness has changed -- re-scale existing data in RAM
/ y  `( u: @, m* F5 q  R) u7 N! K5 k% w" x
         if(oldBrightness == 0) 9 z  m/ {8 ?8 }( z; p# h0 j
                scale = 0; // Avoid /0) _) I  l5 a2 y4 X/ r- o
         else if(b == 255) 2 z9 u  N/ a+ ?9 K/ s9 K) {! A# g
                scale = 65535 / oldBrightness;: U8 [  Q# M2 d. s# `; b
         else
" G- u0 H- I" g3 ]                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;8 [7 o8 M9 E* s$ k5 J" U
1 x2 U' X. p2 K
        for(i=0; i<swObj->PixelLen; i++)  I2 h9 R3 A6 P2 S7 U, Z
         {: u& x% a5 J; V7 i. @
            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  & p  G/ ]& @) x8 T0 J/ L
            c = Sw28_ScaleColor(scale, c);
* q$ @& |2 z/ ?# b5 q3 h5 x            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
- l+ ]& a7 Q2 S8 G* S! L& A. }; G7 ?         }+ L  g0 y" w  u
        swObj->Brightness = newBrightness;" `* c' W0 X: a: \8 z) o3 p4 B
    }
" a8 v6 n; l! V2 C" ]/ U}0 o5 Y1 z, R/ \

4 T0 g) }, W- c- P
! X7 M. ?/ T# e7 b( b6 K
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
3 e- l% F+ M, L; F+ n* R  ~9 V是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。/ c/ m+ W* ?* I1 p* f
比如8颗粒的灯条,那么分配的内存就应该是 ...

: J4 c/ U9 {. S6 M( k* L我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
, c: R! O3 c6 B( m% m9 r$ n4 [当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
7 ?$ U- ^7 r' T: U# K0 b' }按照G R B  传输 为   0x00  0xFF  0x00   
4 X/ e& m) B3 S2 n! x" H+ Z+ L: V) S; k, H3 s1 |
转换为二进制# S; [- ]; S: P: E6 ~+ v
001 001 001 001 001 001 001 001 001 001 001% ]. x& X( D+ Z
110 110 110 110 110 110 110 110 110 110 110
- v) ?# K0 ~: ]+ k7 U9 N. g001 001 001 001 001 001 001 001 001 001 001
2 K! m# L4 y/ g& L9 H1 B/ T+ j6 }
SPI数据为:) x% `. D$ z4 j0 H4 M2 i- V  Q
        0x24                0x92                0x49
/ _) [) Z/ h0 Q- [0 e0010 0100   1001 0010    0100 1001  
# o# Q& y' |0 D0 _        0xDB                0x6D                0xB6( ?5 t6 [) u# N) F
1101 1011   0110 1101    1011 0110
2 h4 a8 K9 Z& v' @) L. J: b. `        0x24                0x92                0x49
  ?3 d/ T  `8 g- ]4 s+ V0010 0100   1001 0010    0100 1001   R' }: ]* T) {, h

. D* ?5 X: S7 A  W6 ?' I7 M  C( I+ w6 \3 ~2 D5 }( l
这里的问题是当我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 K& _1 @# G# c9 [" _% b6 V5 d% t
SPI_DMA_WS281X.rar (584.8 KB, 下载次数: 952)
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- o% s. L; g0 `, ^我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
* l, e: E+ S4 X# [
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。3 V% n) e  M. S* Q' N
好方法。
板子粉丝 回答时间: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 手机版