一、概述
背景:之前在STM32F407的平台是实现了WS2812的驱动,今天移植到STM32F103的平台发现WS2812无法驱动点亮。最终找到了原因:是GPIO模拟时序的原因。
二、过程记录
WS2812的读写时序电平就两种,参考的时序是
代码实现如下
其中GPIO高低电平的实现是通过hal库实现的
#define WS2812_LED(x) do{ x ? \
HAL_GPIO_WritePin(WS2812_GPIO_PORT, WS2812_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(WS2812_GPIO_PORT, WS2812_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED0 = RED */
测试发现一个电平的反转周期大约就是0.4us,所以如果需要延时0.4us的话就不需要插入延时函数了。
按照上述情况修改了延时控制可以驱动WS2812了,但是程序在加入新功能的时候,灯的显示并不是想要驱动的样子而是全亮。如下图所示框选延时是必须要有的。
查找了一些资料,发现WS2812的时序有多种:
ws2812驱动总结(包括对时序的详细分析,代码基于STC15系列单片机)_ws2812时序-优快云博客
上述的代码就是类似参考的这种。
三、优化改善
STM32F103使用 hal库操作GPIO占用时间大约是0.4us,为了缩短这个时间我们可以考虑直接操作寄存器的方式,先看下Hal关于GPIO的操作代码:
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
{
/* Check the parameters */
assert_param(IS_GPIO_PIN(GPIO_Pin));
assert_param(IS_GPIO_PIN_ACTION(PinState));
if (PinState != GPIO_PIN_RESET)
{
GPIOx->BSRR = GPIO_Pin;
}
else
{
GPIOx->BSRR = (uint32_t)GPIO_Pin << 16u;
}
}
更新后的代码如下:
/* LED端口定义 */
//#define WS2812_LED(x) do{ x ? \
// HAL_GPIO_WritePin(WS2812_GPIO_PORT, WS2812_GPIO_PIN, GPIO_PIN_SET) : \
// HAL_GPIO_WritePin(WS2812_GPIO_PORT, WS2812_GPIO_PIN, GPIO_PIN_RESET); \
// }while(0) /* LED0 = RED */
/*方式一*/
#define WS2812_LED(x) do { \
if (x) { \
GPIOB->BSRR = WS2812_GPIO_PIN; \
} else { \
GPIOB->BSRR = (uint32_t)WS2812_GPIO_PIN << 16u; \
} \
} while(0)
/*方式二:*/
//#define WS2812_LED(x) do { GPIOB->BSRR = (x) ? WS2812_GPIO_PIN : (uint32_t)WS2812_GPIO_PIN << 16u; } while(0)
具体寄存器的操作GPIO的延时时间没有测试,用户可根据测试延时时间去设定延时。
其中BSRR对GPIO的操作不会影响到其他IO的电平,参考链接