2471|4

35

帖子

1

TA的资源

一粒金砂(中级)

楼主
 

【GD32L233C-START评测】4、电位器+ws2812做一个简易可调灯色的灯环 [复制链接]

本帖最后由 emmnn 于 2022-3-2 00:02 编辑

前言

不知不觉,春节已经过去一个月了,2022年也过去了两个月。年后因为种种原因,所以评测的事也停了一段时间,今天趁着有空,就又拿起板子捣鼓起来了。

今天的话分享的也不多,就利用电位器加一个WS2812灯环,做一个可调灯色的灯环。使用到的芯片外设主要有两块,一个是模数转换器(ADC),另一个则是串行外设接口(SPI)。

模块说明

旋转电位器用到的是下图的这种,某宝上购买的,内部实际上是一个可调电阻,模拟输出电压0~5V。计划通过读取该模块电压的模拟量来调节WS2812灯环的灯色。

  WS2812灯环,同样也是在某宝上淘来的,由8个WS2812灯珠串联起来的灯环(如下图)。关于WS2812的介绍,简单的说,这是一种可通过编程来实现调节灯色的灯珠,因为它内部集成了控制电路和发光电路。

 按照它的时序要求,我们可以通过发送的数据来控制WS2812C的灯色。以下是它的引脚定义,数据结构,时序图等基本资料

在这里插入图片描述

数据传输方法:

在这里插入图片描述

数据结构:

在这里插入图片描述

数据传输时间:

在这里插入图片描述

时序波形图

在这里插入图片描述

了解完WS2812的基本信息后,接下来就是如何编程控制了。我们可以从上面的时序波形图和波形时序说明图上看到,WS2812C的时序控制要求达到ns级别,如果说使用IO翻转来实现对它的控制,一方面是难以实现(IO翻转的时间不一定有这么快),另一方面是程序的可移植性也会很差,因此,我们这里采用另一种方法,利用芯片自带的SPI+DMA来实现对WS2812的驱动。

SPI驱动原理说明

关于SPI驱动WS2812原理的说明,这里也不详细介绍,网上大把资料,讲得也比较细。简单说,就是通过SPI外设输出一帧数据,通过这帧数据的高低电平(0和1)占比,来模拟上图码型中的0码和1码。举个例子,我通过调节SPI的分频器,来使得SPI输出一帧数据(通常是8位)的时间周期是1us,那么,这个时候,假如我输出的数据是0xF0,那么这一帧数据所对应的码型就是上图中的0码,若是我输出的数据是0xC0,那么这一帧数据所对应的码型就是上图中的1码。另外,之所以选择SPI+DMA的控制方案,则是考虑到如果在频繁调光的情况下,利用DMA可以极大减轻CPU的负担。

编程实现

首先是关于电位器模拟量的读取功能,用到了MCU的ADC外设,引脚的使用如下:

#define ROTATION_SENSOR_GPIO_RCU        (RCU_GPIOB)

#define ROTATION_SENSOR_GPIO_PORT       (GPIOB)
#define ROTATION_SENSOR_GPIO_PIN        (GPIO_PIN_0)

#define ROTATION_ADC_CH                 (ADC_CHANNEL_8)

adc初始化

/*!
    \brief      configure the different system clocks
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void rcu_config(void)
{ 
    /* enable ADC clock */
    rcu_periph_clock_enable(RCU_ADC);
    /* config ADC clock */
    rcu_adc_clock_config(RCU_ADCCK_APB2_DIV6);
}

/*!
    \brief      configure the ADC peripheral
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void adc_config(void)
{
    /* ADC data alignment config */
    adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
    /* ADC channel length config */
    adc_channel_length_config(ADC_REGULAR_CHANNEL, 1U);

    /* ADC trigger config */
    adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);
    /* ADC external trigger config */
    adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);

    /* enable ADC interface */
    adc_enable();
    delay_1ms(1U);
    /* ADC calibration and reset calibration */
    adc_calibration_enable();
}

static void gpio_config(void)
{
    rcu_periph_clock_enable(ROTATION_SENSOR_GPIO_RCU);  
    /* PB0 ADC_IN config */
    gpio_mode_set(ROTATION_SENSOR_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, ROTATION_SENSOR_GPIO_PIN);
}

static uint16_t ADC_Get_Channel(uint8_t channel)
{
    /* ADC regular channel config */
    adc_regular_channel_config(0U, channel, ADC_SAMPLETIME_7POINT5);
    /* ADC software trigger enable */
    adc_software_trigger_enable(ADC_REGULAR_CHANNEL);

    /* wait the end of conversion flag */
    while(!adc_flag_get(ADC_FLAG_EOC));
    /* clear the end of conversion flag */
    adc_flag_clear(ADC_FLAG_EOC);
    /* return regular channel sample value */
    return (adc_regular_data_read());   
}

void rotationSensorInit(void)
{
    gpio_config();
    rcu_config();
    adc_config();
}

读取ADC模拟量

adcval = ADC_Get_Channel(ROTATION_ADC_CH);

接下来是SPI+DMA驱动WS2812部分

先是引脚的定义

#define SPI1_MOSI_RCU            		(RCU_GPIOC)

#define WS2812_RCU_PERIPH				(RCU_SPI1)
#define WS2812_PERIPH					(SPI1)

#define WS2812_MOSI_PORT          		(GPIOC)
#define WS2812_MOSI_PIN           		(GPIO_PIN_12)

#define WS2812_LOW						(0xC0)
#define WS2812_HIGH						(0xF0)

static uint8_t u8TxBuffer[24] = {0};
static uint16_t u16BufferCnt = 0;

SPI+DMA初始化

/**
 *******************************************************************************
 ** \brief Configure SPI DMA function
 **
 ** \param [in] None
 **
 ** \retval None
 **
 ******************************************************************************/
static void Spi_DmaConfig(void)
{
    dma_parameter_struct dma_init_struct;
    /* enable DMA clock */
    rcu_periph_clock_enable(RCU_DMA);

    /* initialize DMA channel 0 */
    dma_deinit(DMA_CH0);
    dma_struct_para_init(&dma_init_struct);
    dma_init_struct.request      = DMA_REQUEST_SPI1_TX;
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr  = (uint32_t)u8TxBuffer;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number       = 24;
    dma_init_struct.periph_addr  = (uint32_t)&SPI_DATA(WS2812_PERIPH);
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH0, &dma_init_struct);
}

void WS2812C_Init(void)
{
    spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(SPI1_MOSI_RCU);
    rcu_periph_clock_enable(WS2812_RCU_PERIPH);

    /* SPI1_MOSI(PC12) GPIO pin configuration */
    gpio_af_set(WS2812_MOSI_PORT, GPIO_AF_5, WS2812_MOSI_PIN);
    gpio_mode_set(WS2812_MOSI_PORT, GPIO_MODE_AF, GPIO_PUPD_NONE, WS2812_MOSI_PIN);
    gpio_output_options_set(WS2812_MOSI_PORT, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, WS2812_MOSI_PIN);

    spi_i2s_deinit(WS2812_PERIPH);

    /* SPI1 parameter config */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;		//CS脚由软件托管
    spi_init_struct.prescale             = SPI_PSC_4;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(WS2812_PERIPH, &spi_init_struct);

    /* configure SPI1 byte access to FIFO */
    spi_fifo_access_size_config(WS2812_PERIPH, SPI_BYTE_ACCESS);

    spi_dma_enable(WS2812_PERIPH, SPI_DMA_TRANSMIT);

    /* enable SPI1 */
    spi_enable(WS2812_PERIPH);

    Spi_DmaConfig();
}

数据发送

static void RGB_Set_Up(void)
{
	u8TxBuffer[u16BufferCnt] = WS2812_HIGH;
    u16BufferCnt++;
}

static void RGB_Set_Down(void)
{
	u8TxBuffer[u16BufferCnt] = WS2812_LOW;
    u16BufferCnt++;
}

void WS2812C_SetRGB(uint32_t RGB888)
{
	int8_t i = 0;
	uint8_t byte = 0;
	
    u16BufferCnt = 0;

	for(i = 23; i >= 0; i--)
	{
		byte = ((RGB888>>i)&0x01);
		if(byte == 1)
		{
			RGB_Set_Up();
		}
		else
		{
			RGB_Set_Down();
		}
	} 

    
    dma_transfer_number_config(DMA_CH0, 24);
    dma_channel_enable(DMA_CH0);

	while (RESET == dma_flag_get(DMA_CH0, DMA_FLAG_FTF))
	{
	}
	dma_flag_clear(DMA_CH0, DMA_FLAG_FTF);
    dma_channel_disable(DMA_CH0);
}

灯环控制接口

void ws2812c_All_Ctrl(uint32_t RGB888)
{
	uint8_t i = 0;
	for(i = 0; i < WS2812C_LED_NUM; i++)
	{
		WS2812C_SetRGB(RGB888);
	}
}

关于模块的驱动部分大致就这样,然后我们在利用板载的WAKEUP KEY按键来转换电位器所控制的RGB灯色,剩下的就是一些应用逻辑功能的实现。最后效果如下:


工程见附件!以上就是今天介绍的全部内容,如有错误,欢迎大家指出!

Template.7z

649.34 KB, 下载次数: 11

此帖出自GD32 MCU论坛

最新回复

感谢分享,这个帖子写的很详细。目前还没有看到错误的地方!   详情 回复 发表于 2022-3-2 10:07
点赞(1) 关注(2)
 

回复
举报

9790

帖子

24

TA的资源

版主

沙发
 

感谢分享,这个帖子写的很详细。

此帖出自GD32 MCU论坛
 
 
 

回复

2

帖子

1

TA的资源

一粒金砂(初级)

板凳
 
littleshrimp 发表于 2022-3-2 09:10 感谢分享,这个帖子写的很详细。

感谢分享,这个帖子写的很详细。目前还没有看到错误的地方!

此帖出自GD32 MCU论坛
 
 
 

回复

35

帖子

1

TA的资源

一粒金砂(中级)

4
 
littleshrimp 发表于 2022-3-2 09:10 感谢分享,这个帖子写的很详细。

此帖出自GD32 MCU论坛
 
 
 

回复

35

帖子

1

TA的资源

一粒金砂(中级)

5
 
yang1114 发表于 2022-3-2 10:07 感谢分享,这个帖子写的很详细。目前还没有看到错误的地方!

此帖出自GD32 MCU论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表