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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
$ o( {4 ^& v  {3 c5 S7 Z. Y# |* n! v* C+ _7 V% h9 G
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html
6 K+ w- B# |! T: ]+ ~$ Y效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。
  t( ]' I# R; [+ G 2017-01-21 09_12_30的屏幕截图.png
; n; `+ ?8 z2 J' Y* V9 G
4 E& @* Z2 n3 v' X/ M$ n仔细一看发现 WS2812一个bit 周期是:
/ i. B1 h) _6 A         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
7 |. K# l; C( r' @* V- r如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。
1 [" z+ G1 K8 @2 _' s# l9 ~+ q  MSPI=0x8 等于 WS2812的 0
* o# a1 [# q+ |& USPI=0xE 等于  WS2812的 1
2 A- S4 b5 Y: x5 l6 T% b也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte
, b5 a) d+ b. G7 D+ N7 ^. Q5 i' A- L( M

, k4 O7 k. R, e$ JSPI速度及极性配置:
- n6 F* ^# a4 g6 w  ~ 捕获.PNG
0 @3 k7 P+ t3 b$ [, E
* H" x/ e; D+ y4 {9 \5 Q- B
& J+ }. u4 k7 V% I( k8 U  @: ]SPI只需要配置主机只发模式,这样可以节省一个IO口
5 G. T6 E+ z0 `; a& }" T* w 捕获2.PNG # R2 a" Q% F- Z3 ^' N! I9 Y
8 n' R+ g. W4 H0 r
驱动整盘WS2812都很轻松
  I% ^9 M% a- \& b5 m 微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
% X! n% W% d3 z8 I" N2 w
/ `: y* s' J0 v驱动库说明:, h$ g* h: O8 @9 a' \
例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程
$ j$ u* O  l) ?$ b# s程序移植了Adafruit NeoPixel库函数,
- B6 b7 J0 b/ m5 P+ C+ K6 _5 y. w采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
) D% X& x  f" [8 o只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。
  {/ B- i2 O" O 捕获3.PNG 3 y7 H- @3 A8 _% ^

! V, B# I& m' T7 X2 N
2 u$ p& c& o, y8 Y) _! m  u% a

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 编辑
* o, N% h! d1 o8 H0 e  ^8 M
翱翔云端的鸟 发表于 2018-4-10 09:45
0 D4 q6 V# h6 k4 M! S  E; M选择数据当做0码 1码的时候  是不是需要 首尾有限制
* a% a. Q3 n3 c# L" h
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps
. K7 I3 C3 i; D
$ p, c" [8 p# K4 Y以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。2 M$ j& e. P; J- p

/ Y) n6 }6 c# l
  1. #define        PIXEL_BYTE_0        (0x92)                                //100100108 ~6 [: e3 }3 Q8 o0 g" n2 i$ g0 d
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    8 e. t% M+ D7 _! m
  3.         #define        PIXEL_BYTE_2        (0x24)                                //001001009 l' t# @& z7 z. q: W
  4. 7 o; F# g$ p6 W8 l8 w1 {) v1 j& z
  5.         uint16_t ws2812_spi::setbuffer(){
    4 q8 g  V7 s+ Y5 X
  6.                 uint8_t *ptr = buf_ptr + 16;, T$ ~& B! h9 M7 R- }
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)0 _4 _: Z2 A% ?! ]) S( F
  8.                         return 0;! I( a' a4 l, x/ i, O: m' U
  9.                 else {
    + N$ u4 ^: S3 \, [$ ~+ _
  10.                         for(int i = 0; i < pixel_count; i ++){
    4 x" P1 p& Y5 m* q2 e, q
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);8 H  `" u: L- q0 U& g
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    4 Q0 r' J5 B, _/ a( K9 ^5 G: g
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);
    # w# O' r3 v6 y- j
  14.                         }. I2 i/ V! P8 Z% C8 M' {! B* p
  15.                 }5 B& `0 y( z' M; G: m2 Z
  16.                 return (ptr - buf_ptr);9 T) v9 v, h2 T8 G/ r
  17.         }6 p. ]9 r# n2 {+ f( L4 }

  18. . b8 D- G- v  b$ p( i
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)
    # }. F4 T+ ?4 r- k! |+ \4 T, z1 |
  20.         {4 {1 g3 j  N- f
  21.                 ptr[0] = PIXEL_BYTE_0;
    - R* V. z/ k1 n! @# t9 ?9 r" x
  22.                 ptr[1] = PIXEL_BYTE_1;  ]  n5 @2 p9 e3 v3 g
  23.                 ptr[2] = PIXEL_BYTE_2;9 R* o5 K, Y# u0 Z  G, @$ {

  24. - @4 J* D4 {! ^
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;% }1 F; |0 \; v0 D) g; f' F
  26.                 value <<= 1;
    * C. e# a3 o$ U( I4 g: F9 e( [
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;  W) L' f6 e& z1 c% \' y
  28.                 value <<= 1;
    & m: M) c1 A0 \0 G* t) g
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;1 l7 d8 N& S+ ^/ \+ e) g
  30.                 value <<= 1;
    5 Z, V7 K2 ~) N- V
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;4 R" I8 u( A8 {) J" T  ~' S+ ^# L" h
  32.                 value <<= 1;- G+ c' K  X9 z3 H
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;
    6 N3 M( c1 n! I1 @) |
  34.                 value <<= 1;
    2 H9 @9 v) R6 o! \  E* P* |' c
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    / X, o: U- Y3 W7 z+ H
  36.                 value <<= 1;
    4 A' w4 o. Q5 ]7 C, S
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;9 u6 r  z  E: g& ~$ L) _
  38.                 value <<= 1;0 N8 _/ x5 {& q( [8 r: y# b+ }, x
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;+ J! c' D: P( L
  40.                 value <<= 1;
    $ @6 v( G3 J$ w1 r

  41. " \3 D2 r) F8 @3 p  W( d( v$ `
  42.                 return (ptr + 3);+ f" ?7 G, `1 b- Y) U& W% _
  43.         }
    - ^/ v2 P- D+ o+ N% C
复制代码
' C+ X, U  }: t8 E1 T  R

8 d8 L* N5 N# V; ]# K
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?2 {9 S2 v, m( t1 N( E+ ~5 a
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
4 J9 R/ ]# s6 V0 e& V2 \{
8 }9 r- }/ w5 U: b8 v2 k$ Z1 g    uint8_t newBrightness = b + 1;6 g; p1 h* {; u$ U
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
9 d- n; `4 M% m' K! r# |+ H/ ~    uint32_t c;
. D( i/ z0 c# X, Z    uint16_t scale,i;
' D* k* S& b  X- o) \, [    if(newBrightness != swObj->Brightness)
; ?! Z  l* o' D$ m: @5 E6 W      {    // Compare against prior value
# t) R& e8 ?% `            // Brightness has changed -- re-scale existing data in RAM
1 e7 g# f$ C7 @# @* q+ \1 s
8 N' q% H( e" [- J         if(oldBrightness == 0)
$ B  J9 v  E" H% p; S                scale = 0; // Avoid /0; @5 m0 M. a  l4 K
         else if(b == 255) ' \8 d/ R( z$ G, C
                scale = 65535 / oldBrightness;
% v0 f2 f, e4 R/ g- }         else ( D, _) f5 ~- N7 O5 T
                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
8 ~* Q* c) B: b& \- k2 [7 C0 m) q/ f# W. ~5 t6 ~" `
        for(i=0; i<swObj->PixelLen; i++)
$ V5 Y$ b5 q: k         {
1 @, l1 ~5 X: D2 C- J- p9 W: Y            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
1 M* z2 a) [: J! L9 M0 }            c = Sw28_ScaleColor(scale, c);' h- V9 P1 Z5 v0 @( S+ O9 ~
            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
4 B% L+ I  s& a& m         }
9 h6 j% p( o9 u        swObj->Brightness = newBrightness;9 b1 T0 q/ ^* S7 c$ w
    }. _' ~; W' h, k8 n/ ?
}6 E( W' |3 b0 T
8 ^! A7 t/ g, ?5 @# }$ w9 w- I

* \$ J9 h4 w  R9 a3 b
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30) S: n- |* f( A! Y
是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。
  y9 a: w8 f. t. u9 w( G1 v- m# }比如8颗粒的灯条,那么分配的内存就应该是 ...
' N$ ]5 r; Y* i( K
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     # o" E1 Y) G0 e) \- T
当我要发送9byte的一个24bit颜色值的时候  比如是传输红色
( Z6 v' |8 H+ f按照G R B  传输 为   0x00  0xFF  0x00   2 }4 w$ u$ v- \) o, D" d* O
! y. G3 S9 E- ^. p8 ^
转换为二进制+ p% V4 h/ @, `$ x
001 001 001 001 001 001 001 001 001 001 001+ q. Q9 F. u. o( S/ J2 F) q
110 110 110 110 110 110 110 110 110 110 110% o  i6 m8 t, ~: W4 [
001 001 001 001 001 001 001 001 001 001 001 * k. C3 C  z" c8 ^" H- u. i
) u+ U( j2 D0 E( x' r- B
SPI数据为:
7 A* g* k1 ?4 [7 u9 t0 T        0x24                0x92                0x49
6 H3 L) k: y; @, f" B. i0010 0100   1001 0010    0100 1001  
4 J# s7 E9 ]$ V9 z5 ?$ O0 K        0xDB                0x6D                0xB61 @. ?$ K  f) J6 X; p, }
1101 1011   0110 1101    1011 0110
0 H/ {: F9 f" O        0x24                0x92                0x493 d$ ]: x$ b$ s# _
0010 0100   1001 0010    0100 1001
8 |" K3 ^/ ?) V6 s
+ X$ O. ^+ d3 k: n
' g, h9 i, q/ W' x这里的问题是当我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后的代码:: U: @7 @/ L& g. @" R# d
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: f/ [$ _. ~0 B1 Y
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...
8 a* R8 k* y. x1 l3 `$ Q5 ^
mdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。
% D; f% R3 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管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版