目录
1、使用引脚
PA7 对应SPI0_Tx, 使用DMA1的通道5
2、变量定义
#define LED_GPIO_RCU RCU_GPIOA
#define LED_GPIO_PERIPH GPIOA
#define LED_GPIO_PIN GPIO_PIN_7
#define LED_ALT_NUM GPIO_AF_5 // 复用
#define LED_SPI_RCU RCU_SPI0
#define LED_SPI SPI0
#define LED_DMA_RCU RCU_DMA1
#define LED_DMA DMA1
#define LED_DMA_CHANNEL DMA_CH5
#define LED_DMA_SUBPERI DMA_SUBPERI3
#define CODE0 0xE0 // 0码, 发送的时间 1110 0000 根据不同的SCK适当调整
#define CODE1 0xF8 // 1码, 发送的时间 1111 1000
const RGBColor_TypeDef RED = {255,0,0};
const RGBColor_TypeDef GREEN = {0,255,0};
const RGBColor_TypeDef BLUE = {0,0,255};
const uint16_t PIXEL_NUM = 8; // 灯珠 RGB数量
uint8_t pixelBuffer[PIXEL_NUM][24] ;// 灯珠
3、GPIO初始化
void AlarmLed_GPIOInit(void)
{
rcu_periph_clock_enable(LED_GPIO_RCU); /* 使能 GPIOA的时钟 */
gpio_af_set (LED_GPIO_PERIPH, LED_ALT_NUM, LED_GPIO_PIN);
gpio_mode_set (LED_GPIO_PERIPH, GPIO_MODE_AF, GPIO_PUPD_NONE, LED_GPIO_PIN);
gpio_output_options_set(LED_GPIO_PERIPH, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, LED_GPIO_PIN);
}
4、SPI初始化
void AlarmLed_SPIInit(void)
{
rcu_periph_clock_enable(RCU_SPI0); /* 使能 SPI的时钟 */
/* SPI 配置 */
spi_disable(LED_SPI);
spi_parameter_struct spi_init_struct;
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; /* 设置传输模式 */
spi_init_struct.device_mode = SPI_MASTER; /* 设置工作模式 */
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT; /* 设置SPI的数据大小:SPI发送接收8位帧结构 */
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE; /* 串行同步时钟的空闲状态为低电平 */
spi_init_struct.nss = SPI_NSS_SOFT; /* nss 软件控制 */
spi_init_struct.prescale = SPI_PSC_16; /* 定义波特率预分频的值 */
spi_init_struct.endian = SPI_ENDIAN_MSB; /* 传输方式大端字节 */
spi_init(LED_SPI, &spi_init_struct); /* 初始化 */
spi_enable(LED_SPI); /* 使能 SPI */
/* SPI/I2S 配置 */
spi_dma_enable(LED_SPI, SPI_DMA_TRANSMIT);
}
WS2812B编码协议(单位:ns):
min typ max
bit 0
T0H: 220 - 420
T0L: 750 - 1600
bit 1
T1H: 750 - 1600
T1L: 220 - 420
RESET: time > 300us
SPI0 --> APB2 时钟 120MHz
16分频APB2,120MHz/16 = 7.5MHz
时钟周期为:1/7.5/1e6 = 1.33e-7 = 133ns
0码: 111(3*133=399)0 0000(5*133=665) --> 0xE0
1码: 1111 1(5*133=665) 000(3*133=399) --> 0xF8
算出的时间与手册的略微有点出入,但是经过测试,SPI使用16分频,0码为0xE0,1码为0xF8,灯光显示最为稳定
5、DMA初始化
void AlarmLed_DMAInit(void)
{
rcu_periph_clock_enable(LED_DMA_RCU); /* 使能 SPI的时钟 */
dma_single_data_parameter_struct dma_init_SPI0_TX={0};
/* DMA 配置 */
dma_deinit(LED_DMA, LED_DMA_CHANNEL); /* SPI0_Tx --> DMA1 Channel5 */
dma_init_SPI0_TX.periph_addr = (uint32_t)(&SPI_DATA(LED_SPI)); /* DMA外设ADC基地址 */
dma_init_SPI0_TX.periph_inc = DMA_PERIPH_INCREASE_DISABLE; /* 外设地址寄存器不变 */
dma_init_SPI0_TX.memory0_addr= (uint32_t)pixelBuffer; /* DMA内存基地址 */
dma_init_SPI0_TX.memory_inc = DMA_MEMORY_INCREASE_ENABLE; /* 内存地址寄存器递增 */
dma_init_SPI0_TX.periph_memory_width = DMA_PERIPH_WIDTH_8BIT; /* 外设数据传输宽度 */
dma_init_SPI0_TX.direction = DMA_MEMORY_TO_PERIPH; /* 数据传输方向,从内存读取发送到外设 */
dma_init_SPI0_TX.number = PIXEL_NUM * 24; /* DMA通道的DMA缓存的大小 */
dma_init_SPI0_TX.priority = DMA_PRIORITY_HIGH; /* DMA通道拥有中优先级 */
dma_init_SPI0_TX.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
dma_single_data_mode_init(LED_DMA, LED_DMA_CHANNEL, &dma_init_SPI0_TX);
dma_channel_subperipheral_select(LED_DMA, LED_DMA_CHANNEL, DMA_SUBPERI3);
dma_channel_enable(LED_DMA, LED_DMA_CHANNEL);
}
需要注意的是
dma_channel_subperipheral_select(LED_DMA, LED_DMA_CHANNEL, DMA_SUBPERI3);
该函数的第三个参数是通道外设的值
6、初始化
void AlarmLed_Init()
{
AlarmLed_GPIOInit();
AlarmLed_SPIInit();
AlarmLed_DMAInit();
}
7、设定某个RGB LED的颜色
void rgb_SetColor(uint16_t LedId, RGBColor_TypeDef Color)
{
uint16_t i;
if( LedId > ( PIXEL_NUM ) )
{
return;
}
for(i=0;i<=7;i++)
{
pixelBuffer[LedId][i]= ( (Color.G & (1 << (7 -i)) )? (CODE1):CODE0 );
}
for(i=8;i<=15;i++)
{
pixelBuffer[LedId][i]= ( (Color.R & (1 << (15-i)) )? (CODE1):CODE0 );
}
for(i=16;i<=23;i++)
{
pixelBuffer[LedId][i]= ( (Color.B & (1 << (23-i)) )? (CODE1):CODE0 );
}
}
8、DMA发送数据
void rgb_SendArray(void)
{
if(dma_flag_get(LED_DMA, LED_DMA_CHANNEL, DMA_FLAG_FTF) != RESET) /* 等待DMA传输完成 */
{
dma_flag_clear(LED_DMA, LED_DMA_CHANNEL, DMA_FLAG_FTF); /* 清除传输完成标志 */
dma_channel_disable(LED_DMA, LED_DMA_CHANNEL); /* 关闭DMA传输 */
dma_transfer_number_config(LED_DMA, LED_DMA_CHANNEL, PIXEL_NUM * 24); /* 数据传输量 */
dma_channel_enable(LED_DMA, LED_DMA_CHANNEL); /* 开启DMA传输 */
}
}
9、设置红灯
void RGB_RED(uint16_t Pixel_LEN)
{
uint16_t i;
for(i = 0; i < Pixel_LEN; i++)
{
rgb_SetColor(i,RED);
}
rgb_SendArray();
}