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

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

[复制链接]
waiman 发布时间:2018-2-6 15:03
本帖最后由 waiman-156411 于 2018-2-6 17:57 编辑
" j2 }3 T5 u2 K9 k# Q0 `$ T5 g1 ~* f$ e/ }6 x
看到zoomdy 的驱动例子启发 https://www.stmcu.org.cn/module/forum/thread-610279-1-1.html3 ?" }, n7 W" \  i* A& \& }
效果虽然不错,可以节省不少MCU资源,但用一个byte的SPI数据代替WS2812一个bit,很浪费RAM资源,而且生成像素的效率也很慢。9 l3 A' h" i- F8 ~3 |4 t
2017-01-21 09_12_30的屏幕截图.png
4 J0 K$ z8 |. O8 }( s) \
& E7 B+ f: p. Q0 _仔细一看发现 WS2812一个bit 周期是: 6 H6 [, }$ J8 y7 \0 Z3 T3 D- _
         TH+TL=1250ns (±600ns), Min=650ns,Max=1850ns
. k. x0 ~# X& [' }+ r' E9 |如果把SPI提速到4Mbits/S 的时候,SPI一个bit的周期是250ns,刚好和符合WS2812 T0H或T1L 400ns ±150ns范围。SPI 4个bit的周期刚好1000ns,刚好可以满足WS2812的时序需求。, }6 O7 G- @5 z) r) U, Y
SPI=0x8 等于 WS2812的 0
6 j' F! w' D5 z5 oSPI=0xE 等于  WS2812的 17 v( X& b5 k6 r/ a! r+ h3 ~9 w
也就变成一个SPI的byte,可以表示2个Ws2812的2个bit。一RGB像素颜色只需要 24bit/2bit = 12byte; `% C; D1 M% T# R/ q
0 Q' q9 y/ r7 G8 P- X! y' {( E

, v$ d: b# Z- o2 kSPI速度及极性配置:
+ u% q/ @' P  W 捕获.PNG 9 w7 W& Q( W1 `

" p: D3 `' E- J4 l8 r  m6 z& t5 C7 n* ^% _1 d4 ~8 J* m
SPI只需要配置主机只发模式,这样可以节省一个IO口1 j/ f2 L" T9 n
捕获2.PNG
+ e: \1 Z! F, l4 h# D; r
3 U  A! w3 I+ E1 m4 i# c- t% [+ a驱动整盘WS2812都很轻松0 A$ |' `0 Y" }
微信图片_20180206141317.jpg    微信图片_20180206135738.jpg
: ]1 ?) e5 ^8 g, Z, p
2 |) e( p  L2 J; [3 u5 H驱动库说明:
: O- m+ t5 L/ H4 z# N: k# F, s例子使用Stm32CubMx 4.23,STM32L151C8T6,IAR/MDK工程# W8 M6 B. n! |- I2 S% B
程序移植了Adafruit NeoPixel库函数,
  R# D  L9 d: |2 N- d采用HAL库驱动方式(struct),可以方便移植到其他STM32芯片上,
, |6 u" d; ^! c" c2 P只需要分配多个struct变量,硬件稍微改下,就能分时复用,控制多串灯珠。! q  t) }! c9 S0 V: t! d( F$ V
捕获3.PNG : p4 y5 G& D# C- ]
$ {* b) y) C; |( o5 Q  e1 ?& @1 J3 g
4 d6 W0 ]  b' j

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 编辑 6 o1 K# i/ d8 U0 o# i3 I
翱翔云端的鸟 发表于 2018-4-10 09:45) Y, r" S# P; t5 }
选择数据当做0码 1码的时候  是不是需要 首尾有限制
3 u6 a* j! U* d" v3 F
用3位代表1个bit通过SPI方式驱动WS2812时,SPI的速度请调整到2.25Mbps+ t: Y6 ?4 A3 U" a2 }, N6 L& X

7 W  K* g; j( |  `以下是我用的代码,供你参考。ptr指向SPI Buf,value 是要转码的值。% }( ^/ F: h# w8 M3 ?$ a: k
! T1 G0 b5 x5 E- o+ r
  1. #define        PIXEL_BYTE_0        (0x92)                                //10010010
    : @7 O. M  K3 t" Q0 X  j
  2.         #define        PIXEL_BYTE_1        (0x49)                                //01001001
    0 W9 x! ^: `: i
  3.         #define        PIXEL_BYTE_2        (0x24)                                //00100100
    " `7 v& u# r6 s. m4 B
  4. 6 U3 g' Z6 z$ D1 s, x4 D
  5.         uint16_t ws2812_spi::setbuffer(){8 D2 v" s9 U, Q$ i+ \
  6.                 uint8_t *ptr = buf_ptr + 16;% m0 f3 Z6 @; p. w/ L3 f0 H/ T. `
  7.                 if(pixel_count == 0 || pixels == NULL || buf_ptr == NULL)
    & p+ d. l9 V. L+ T6 s" H, o
  8.                         return 0;
    2 @5 X/ L  D$ y6 R  A6 x% s
  9.                 else {. l/ S4 D0 o6 T. ?8 d3 f
  10.                         for(int i = 0; i < pixel_count; i ++){
    . K7 n5 ~: p7 H& X0 L6 x
  11.                                 ptr = setPixelBuf(ptr, (pixels[i].green) * pixels[i].brightness / 0x100);+ r& a0 e! {+ d
  12.                                 ptr = setPixelBuf(ptr, (pixels[i].red) * pixels[i].brightness / 0x100);
    , p+ E5 e: c- ?5 w6 C3 Q
  13.                                 ptr = setPixelBuf(ptr, (pixels[i].blue) * pixels[i].brightness / 0x100);& ]( I! c( I- `0 M. z' S
  14.                         }
    ; |; b9 j" q- Z7 Q' [2 Z
  15.                 }7 ?4 B) U& v4 Y1 _( |: p* R5 l
  16.                 return (ptr - buf_ptr);$ R' X* c2 E6 H8 N6 e
  17.         }, p8 j( }$ E; E8 H' a# M5 D
  18. 1 s: v8 w+ F  I4 s! G9 C
  19.         uint8_t* ws2812_spi::setPixelBuf(uint8_t *ptr, uint8_t value)5 s+ d8 T6 l$ o5 A( a
  20.         {
    0 {; E- H* E7 O3 y  U
  21.                 ptr[0] = PIXEL_BYTE_0;
    * e/ ]) @" h# @  b
  22.                 ptr[1] = PIXEL_BYTE_1;
    2 v" d  E9 I3 H$ o+ b$ f3 `- N
  23.                 ptr[2] = PIXEL_BYTE_2;! y8 \, K* r1 F/ U9 U* ]

  24. ) _7 }9 N  @- w
  25.                 ptr[0] |= (value & 0x80)?(0x40):0x00;8 u. O6 H* s/ g- }' b0 Z
  26.                 value <<= 1;
    + `5 O( Q+ F( {# T: a" a
  27.                 ptr[0] |= (value & 0x80)?(0x08):0x00;
    8 x" q- p1 q/ t' j0 t
  28.                 value <<= 1;
    - |  M2 j$ w. k
  29.                 ptr[0] |= (value & 0x80)?(0x01):0x00;
    . t1 H! U/ a0 _/ h
  30.                 value <<= 1;, u3 g( h( [# K: [- E
  31.                 ptr[1] |= (value & 0x80)?(0x20):0x00;
    ( E  h5 \4 I. Z% t% v/ N4 P; _4 g2 p
  32.                 value <<= 1;: [/ W0 S" p' u7 K& W
  33.                 ptr[1] |= (value & 0x80)?(0x04):0x00;! }" V- c& Z* h0 o& ]. z
  34.                 value <<= 1;* {4 Z* X. q( n" N+ z) ?& ]; U
  35.                 ptr[2] |= (value & 0x80)?(0x80):0x00;
    7 ~. R, v$ m& A2 h* ]/ t; W
  36.                 value <<= 1;
    + H5 t$ x* N' H* d) O1 @
  37.                 ptr[2] |= (value & 0x80)?(0x10):0x00;
    ! `# T, P- ]6 F9 D. L, U+ ~
  38.                 value <<= 1;
    . l1 ~/ p7 X; W! O( H3 Q
  39.                 ptr[2] |= (value & 0x80)?(0x20):0x00;! D9 N8 e# y2 }& k: I: ^' S( P- j
  40.                 value <<= 1;9 {8 Y) U. L$ W! [: E8 V" V7 J1 M8 K
  41. , T; T4 ^, s8 k
  42.                 return (ptr + 3);
    # K. U5 E, e' m' h, o" h
  43.         }& n+ i% x; o, x, X+ `" [
复制代码
/ a7 M, |1 o2 ~

6 R0 Q; |" f( f# p; N
Yv-Yu 回答时间:2019-12-5 23:56:10
首先膜拜一下大佬,感谢大佬的分享,然后想请教一下各位,下面这段代码的实现是个啥意思啊,scale是干啥的啊?7 v9 X7 f3 _- x* J- {5 p! L9 ?
void Sw28_SetBrightness(SW28_HandleTypeDef *swObj, uint8_t b)
- t/ k/ b( r2 [8 x) W1 x{9 B% u3 R: G9 W( d4 C) k6 M" |
    uint8_t newBrightness = b + 1;% g4 E6 q" C- C' d$ M! `; h
    uint8_t  oldBrightness = swObj->Brightness - 1; // De-wrap old brightness value
! b1 p; S8 Z: G4 _    uint32_t c;
- f' z+ P. s$ L/ d    uint16_t scale,i;# E+ M7 x" G: i/ f& h) O
    if(newBrightness != swObj->Brightness)
8 u+ [1 W5 M" G+ G9 s- @! D. w      {    // Compare against prior value2 r: R/ P, n8 H* B. C
            // Brightness has changed -- re-scale existing data in RAM
# m) I3 J5 g; s, O* B0 @3 R( b/ z' b' M% n$ {: o% ^
         if(oldBrightness == 0)
3 b) I8 @0 g& |9 b2 f" v                scale = 0; // Avoid /0) \2 z6 q, Z8 w  D" ~, N( `8 b
         else if(b == 255)
: l$ h, F( j: w$ H' \                scale = 65535 / oldBrightness;# s0 Q2 o; m/ E  u& t
         else
9 V: q2 A& q3 i. u5 r9 e) W5 f- Z                scale = (((uint16_t)newBrightness << 8) - 1) / oldBrightness;
9 t# ^4 g1 w9 D. Q1 I* }  a% X
! z% y) Z4 ~7 a5 U9 L0 Q        for(i=0; i<swObj->PixelLen; i++)
  ?  s! K! h, C! I1 K2 c         {
! O3 s' d% ~  Q& y            c = Sw28_GetPixelColor(swObj,i);        //获取16bit颜色  
# M  `& x* c1 ^2 q1 i            c = Sw28_ScaleColor(scale, c);
- ~" F6 \; |+ d3 c2 e            Sw28_SetPixelsColor(swObj,i,c);//设置颜色
& G8 r9 B. H' j& o* h( l. s: [         }
* A* i0 O" K  [+ a/ w        swObj->Brightness = newBrightness;
) \. P/ |' d! c: k+ C9 d    }+ K5 c8 i$ z4 F3 S  L- |# _
}; o/ S& L3 `9 @
) u. v& x! e# ~! a0 P! B/ y
( H; V! l8 M7 K  Z$ t
翱翔云端的鸟 回答时间:2018-4-10 09:42:55
jjbboox 发表于 2018-4-10 07:30
; I, W5 o3 x5 ?4 R3 l是的,其实两头各还要加一个RESET信号,大概在32个字节左右吧。' ~6 g4 |& D4 b6 O
比如8颗粒的灯条,那么分配的内存就应该是 ...
+ x+ C, [+ X. p: _& M# `9 l. A$ J
我现在遇到一个问题    3bit表示一个code     001  表示0code         110表示1code     
8 N: }9 H) u, Y( q/ e当我要发送9byte的一个24bit颜色值的时候  比如是传输红色- k2 s  P* e9 Q! Z4 l1 u- }2 F
按照G R B  传输 为   0x00  0xFF  0x00   
/ A+ X7 i( C3 J5 e5 x9 c% q
) W' j3 F) \( Z7 }转换为二进制4 y' @, r. }: N8 x/ R+ t
001 001 001 001 001 001 001 001 001 001 0017 E. C" q9 k: e' [
110 110 110 110 110 110 110 110 110 110 110
4 N3 r; y9 V8 o. K001 001 001 001 001 001 001 001 001 001 001 % H& T! D2 c( }  j9 b$ f9 q
. V  a0 |+ f* D7 D4 v
SPI数据为:
, |. E8 I1 Z: z        0x24                0x92                0x49
8 U& z; Q- ^* D5 r0010 0100   1001 0010    0100 1001  
7 K- ^: W. p+ J        0xDB                0x6D                0xB6
2 l/ M6 V6 d) B# p3 ~0 z( \  z# C1101 1011   0110 1101    1011 0110; i/ i% o, Z5 x
        0x24                0x92                0x495 Y. ?$ x5 A# t, K
0010 0100   1001 0010    0100 1001 0 B8 c+ A) S+ ]& q

' T0 r% W, b: o- V0 r
6 b4 E: ~: u  K8 U8 N这里的问题是当我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后的代码:/ c3 O9 R! J1 \
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( P: h5 x# {/ n7 i, }% b% q
我只想知道楼主图中的代码显示是MDK的还是其他文本编辑器的,如果是MDK的话配置高亮的配置文件是否可以发我 ...

; ~) h, `: N+ V4 e# Jmdk的,论坛里有人分享过了,你可以搜一下
Hackerpro 回答时间:2018-2-26 14:32:08
非常棒的想法,赞一个
jjbboox 回答时间:2018-2-26 15:14:01
厉害,这个帖子要收藏起来的。+ N, S# m; M+ a6 F( F. Y3 J, u/ G- W
好方法。
板子粉丝 回答时间: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 手机版