2330 views|4 replies

35

Posts

1

Resources
The OP
 

[GD32L233C-START Review] 4. Potentiometer + ws2812 to make a simple light ring with adjustable light color [Copy link]

This post was last edited by emmnn on 2022-3-2 00:02

Preface

Unconsciously, a month has passed since the Spring Festival, and two months have passed in 2022. Due to various reasons after the New Year, the evaluation was stopped for a while. Today, I took advantage of my free time and picked up the board to tinker with it again.

Today, I won’t share much, but I will use a potentiometer and a WS2812 light ring to make an adjustable light ring. There are two main chip peripherals used, one is the analog-to-digital converter (ADC), and the other is the serial peripheral interface (SPI).

Module Description

The rotary potentiometer used is the one in the picture below, which was purchased on Taobao. It is actually an adjustable resistor inside, with an analog output voltage of 0~5V. The plan is to adjust the color of the WS2812 light ring by reading the analog value of the module voltage.

The WS2812 light ring, which was also bought on Taobao, is a light ring made of 8 WS2812 lamp beads connected in series (as shown below). To put it simply, WS2812 is a lamp bead that can be programmed to adjust the light color because it integrates the control circuit and the light-emitting circuit.

According to its timing requirements, we can control the light color of WS2812C by sending data. The following is its pin definition, data structure, timing diagram and other basic information

insert image description here

Data transfer method:

insert image description here

Data structure:

insert image description here

Data transfer time:

insert image description here

Timing waveform

insert image description here

After understanding the basic information of WS2812, the next step is how to program and control it. We can see from the above timing waveform and waveform timing description that the timing control of WS2812C is required to reach the ns level. If we use IO flip to control it, it is difficult to achieve on the one hand (IO flip time may not be so fast), and on the other hand, the portability of the program will be very poor. Therefore, we use another method here to use the chip's built-in SPI+DMA to drive WS2812.

SPI driver principle description

The explanation of the principle of SPI driving WS2812 is not introduced in detail here. There are a lot of materials on the Internet, and they are also quite detailed. Simply put, a frame of data is output through the SPI peripheral, and the proportion of high and low levels (0 and 1) of this frame of data is used to simulate the 0 code and 1 code in the above code. For example, I adjust the SPI divider to make the time period of SPI outputting a frame of data (usually 8 bits) 1us. Then, at this time, if the data I output is 0xF0, then the code pattern corresponding to this frame of data is the 0 code in the above figure. If the data I output is 0xC0, then the code pattern corresponding to this frame of data is the 1 code in the above figure. In addition, the reason for choosing the SPI+DMA control scheme is that if the light is frequently dimmed, the use of DMA can greatly reduce the burden on the CPU.

Programming Implementation

First, the analog reading function of the potentiometer uses the ADC peripheral of the MCU. The pin usage is as follows:

#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 initialization

/*!
    \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();
}

Read ADC analog value

adcval = ADC_Get_Channel(ROTATION_ADC_CH);

Next is the SPI+DMA driver WS2812 part

First, the definition of the pin

#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 initialization

/**
 *******************************************************************************
 ** \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();
}

Data transmission

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);
}

Light ring control interface

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

That's about it for the module's driver. Then we use the onboard WAKEUP KEY button to switch the RGB light color controlled by the potentiometer. The rest is the implementation of some application logic functions. The final effect is as follows:


The project is attached! The above is all the content introduced today. If there are any errors, please point them out!

Template.7z

649.34 KB, downloads: 11

This post is from GD32 MCU

Latest reply

Thanks for sharing, this post is very detailed. I haven't seen any mistakes yet!   Details Published on 2022-3-2 10:07
 

9720

Posts

24

Resources
2
 

Thanks for sharing, this post is very detailed.

This post is from GD32 MCU
 
 
 

2

Posts

1

Resources
3
 
littleshrimp posted on 2022-3-2 09:10 Thanks for sharing, this post is very detailed.

Thanks for sharing, this post is very detailed. I haven't seen any mistakes yet!

This post is from GD32 MCU
 
 
 

35

Posts

1

Resources
4
 
littleshrimp posted on 2022-3-2 09:10 Thanks for sharing, this post is very detailed.

This post is from GD32 MCU
 
 
 

35

Posts

1

Resources
5
 
yang1114 posted on 2022-3-2 10:07 Thanks for sharing, this post is very detailed. I haven't seen any mistakes yet!

This post is from GD32 MCU
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list