ARM嵌入式学习笔记(10)SPI通信
SPI简介
SPI 协议是摩托罗拉公司开发的一种高速的,全双工,同步的通信总线,使用四根线进行通信,具有简单易用,通讯速度高的特点。SPI 总线上可以挂载多个设备,这些设备被区分成主设备(Master)和从设备(Slave),主设备通过时钟线和片选线对从设备进行控制。
SPI 协议所使用到的引脚及其功能见下表
SPI 是一种全双工的通信协议,主设备和从设备通信时,两端的收发是同步进行的,即主设备和从设备在向对方发送数据的同时,也在接收对方发来的数据。
SPI 的通信过程如下:
- 主设备将要进行通讯的从设备的 SS/CS 片选拉低,
- 主设备通过 SCK 向从设备提供同步通讯所需要的时钟信号。
- 主设备通过 MOSI 向从设备发送 8 位数据,同时通过 MISO 接收从设备发来的 8 位数
据。 - 通信结束,主设备拉高 SS/CS 片选。
SPI在CubeMX中的配置
这里我们只驱动WS2812,所以选择主机发送模式即可
WS2812代码
//ws2812.h
#ifndef __WS2812_H__
#define __WS2812_H__
#include <stdint.h>
// 编码 0 : 11000000
#define CODE_0 0xC0
// 编码 1 : 11111000
#define CODE_1 0xF8
/*ws2812b灯珠数量*/
#define WS2812B_AMOUNT 9
typedef struct
{
uint8_t R;
uint8_t G;
uint8_t B;
} tWs2812bCache_TypeDef;
/**
* @brief HSV颜色空间
*/
typedef struct {
float h; // 色调H(hue) 0~360° {R(0°),G(120°),B(240°)}
float s; // 饱和度S(saturation) 0~1.0
float v; // 明度V(value) 0~1.0
}color_hsv_t;
typedef struct {
unsigned char r; // 0-255
unsigned char g; // 0-255
unsigned char b; // 0-255
unsigned int color; // 0x000000 ~ 0xffffff
}color_rgb_t;
extern tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT];
/**
* @brief RGB颜色空间 转 HSV颜色空间
* @param rgb:RGB颜色空间参数
* @param hsv:HSV颜色空间参数
* @return none
*/
void rgb2hsv(color_rgb_t *rgb, color_hsv_t *hsv);
/**
* @brief HSV颜色空间 转 RGB颜色空间
* @param hsv:HSV颜色空间参数
* @param rgb:RGB颜色空间参数
* @return none
*/
void hsv2rgb(color_hsv_t *hsv, color_rgb_t *rgb);
void WS2812b_Set(uint16_t Ws2b812b_NUM, uint8_t r,uint8_t g,uint8_t b);
void WS2812B_Task(void);
#endif
//ws2812.c
#include "ws2812.h"
#include "spi.h"
//灯条显存SPI数据缓存
uint8_t gWs2812bDat_SPI[WS2812B_AMOUNT * 24] = {0};
//灯条显存
tWs2812bCache_TypeDef gWs2812bDat[WS2812B_AMOUNT] = {
//R G B
0XFF, 0X00, 0X00, //0
0X00, 0XFF, 0X00, //1
0X00, 0X00, 0XFF, //2
0X00, 0XFF, 0XFF, //3
0XFF, 0X00, 0XFF, //4
0XFF, 0XFF, 0X00, //5
0XFF, 0XFF, 0XFF, //6
0X00, 0X00, 0X00, //7
0XFF, 0X00, 0X00, //8
};
void WS2812b_Set(uint16_t Ws2b812b_NUM, uint8_t r,uint8_t g,uint8_t b)
{
uint8_t *pR = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24 + 8];
uint8_t *pG = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24];
uint8_t *pB = &gWs2812bDat_SPI[(Ws2b812b_NUM) * 24 + 16];
for(uint8_t i = 0; i < 8; i++) {
if(g & 0x80) {
*pG = CODE_1;
}
else {
*pG = CODE_0;
}
if(r & 0x80) {
*pR = CODE_1;
}
else {
*pR = CODE_0;
}
if(b & 0x80) {
*pB = CODE_1;
}
else {
*pB = CODE_0;
}
r <<= 1;
g <<= 1;
b <<= 1;
pR++;
pG++;
pB++;
}
}
void WS2812B_Task(void)
{
uint8_t dat = 0;
//将gWs2812bDat数据解析成SPI数据
for(uint8_t iLED = 0; iLED < WS2812B_AMOUNT; iLED++)
{
WS2812b_Set(iLED, gWs2812bDat[iLED].R, gWs2812bDat[iLED].G, gWs2812bDat[iLED].B);
}
//总线输出数据
HAL_SPI_Transmit(&hspi1, gWs2812bDat_SPI, sizeof(gWs2812bDat_SPI),0XFFFF);
//使总线输出低电平
HAL_SPI_Transmit(&hspi1, &dat, 1,0XFFFF);
//帧信号:一个大于50us的低电平
HAL_Delay(1);
}
只要滚动刷新显存内容就可以实现多灯串联,如果想要调色可以使用hsv通道转换到rgb通道,这样控制亮度和颜色会比较方便。