本帖最后由 袁胜富 于 2023-9-11 20:11 编辑
#申请原创# #有奖活动# #技术资源# #申请开发板#@21小管家 file:///C:/Users/hasee/AppData/Local/Temp/msohtmlclip1/01/clip_image001.png
一、概述
CH32X035的PIOC是其众多MCU产品中的一个亮点,它可以做很多事情。引用手册:CH32X035 芯片内嵌了一个可编程协议 I/O 微控制器,该微控制器基于单时钟周期的专用精简指令集 RISC 内核,运行于系统主频,具有 2K 指令的程序 ROM 和 49 个 SFR 寄存器及 PWM 定时/计数器,支持 2 个 I/O 引脚的协议控制。从手册描述来看PIOC的结构就是一个定时器,可以输出PWM也可以捕获。在手册中没有关于PIOC的原理框图和相关寄存器说明,但是官方的SDK包里有关于PIOC的SFR.h头文件以及关于PIOC驱动WS2812和DS1820的例程。
本人就WS2812例程进行了二次封装,并且发现一些BUG,于是乎就有了本篇文章。
二、原理
从标准库文件来看PIOC外设挂载在AHB总线,使用系统时钟最大48Mz,PIOC在驱动WS2812时,如果使用48MHz,则需要56分频,约等于1.16us。如果使用24MHz,则需要28分频,约等于1.16us,也就是PIOC每发1bit数据需要1.16us周期。系统的时钟选择在system_ch32x035.c文件中选择,以下代码就是选择48MHz。//#define SYSCLK_FREQ_8MHz_HSI 8000000
//#define SYSCLK_FREQ_12MHz_HSI 12000000
//#define SYSCLK_FREQ_16MHz_HSI 16000000
//#define SYSCLK_FREQ_24MHz_HSI 24000000
#define SYSCLK_FREQ_48MHz_HSI HSI_VALUE
因为PIOC复用了系统20KB的4KBSRAM,所以需要在Link.ld文件中修改一下,具体修改如图1所示。
图1
从手册来看,PIOC拥有两个IO口,分别是IO0和IO1,IO0连接PC18引脚,IO1连接在PC19引脚(复用在PC7引脚)。因为PC18和PC19分别是SWDIO和SWCLK下载引脚,所以感觉有点不太OK,会占用下载接口。
在初始化PC18作为IO0是,需要使用GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);//禁用下载功能
禁用下载功能。
从例程中知道PIOC的RAM占3KB字节数据,也就是3072字节。
通过阅读WS2812的资料我们得知一个灯的数据由24bit组成,GRB中G占8位,R占8位,B占8位,G代表绿色,R代表红色,B代表蓝色,通过三种颜色可以显示出我们要的颜色。GRB顾名思义就是绿色占最低8位,红色占中间位,蓝色占最高8位,一颗灯珠驱动需要24bit数据,也就是3个字节,从PIOC来看RAM来看,3072/3=1024,意味着PIOC可以同时驱动1024个灯珠。
总结如下:PIOC要驱动WS2812需要按以下步骤进行配置和操作
将Link.ld文件的SRAM修改为16K;
修改系统时钟为24MHz(原因是48MHz第一个灯珠不亮).
因为PIOC的IO0占用下载口的SWDIO,需要使用USB下载。
三、代码
PIOC_WS2812.h
#ifndefWS2812_PIOC_WS2812_H_
#define WS2812_PIOC_WS2812_H_
#ifndef __SPI_DMA_WS2812_H
#define __SPI_DMA_WS2812_H
#include"ch32x035.h"
#include"PIOC_SFR.h"
#define USE_24MHZHSI 1
//RGB灯的数量
#define PIXEL_NUM1 2 //灯带1灯数(组数)
#define PIXEL_NUM2 2 //灯带2灯数(组数)
#define PIXEL_NUM3 2 //灯带3灯数(组数)
#define PIXEL_NUM4 2 //灯带4灯数(组数)
#define PIXEL_NUM (PIXEL_NUM1+PIXEL_NUM2+PIXEL_NUM3+PIXEL_NUM4)//灯数统计
#define GRB 24 //3*8 //Red8位 Green8位 Blue8位加起来24位
#define RGB1W_SFR_ADDR ((uint8_t *)&(PIOC->D8_DATA_REG0)) //RGB1W特殊功能寄存器模式的数据缓冲器起始地址详见"PIOC_SFR.h"的PIOC_TypeDef结构体
#define RGB1W_SFR_SIZE 32 //RGB1W特殊功能寄存器模式的数据缓冲器起始地址宽度
#define RGB1W_RAM_ADDR ((uint8_t *)(PIOC_SRAM_BASE+0x400)) //RGB1W随机访问存储器模式的数据缓冲器起始地址
#define RGB1W_RAM_SIZE ((uint8_t *)(PIOC_SRAM_BASE+0x1000)-RGB1W_RAM_ADDR) // RGB1W随机访问存储器模式的数据缓冲器起始地址宽度 3KB大小复用了4KB的系统SRAM
#define RGB1W_FREQ_CFG (0x000C*2) //RGB1W的系统频率配置
#define RGB1W_CYC_48M 56 //位周期系统频率48MHZ ,约等于1.16us每个周期
#define RGB1W_CYC_24M 28 //位周期系统频率24MHZ ,约等于1.16us每个周期
#define RGB1W_CMD_RAM 0x80 //通知PIOC从RAM发送数据
#define RGB1W_COMMAND (PIOC->D8_CTRL_WR) //输入指令
// 1~RGB1W_SFR_SIZE: RGB SFR mode, 0x01-0x20: RGB with short data in SFR, low 7bits is total byte
// > RGB1W_CMD_RAM: RGB RAM mode, 0x88-0xFC: RGB with long data in code RAM, low 7bits is RGB1W_CYC_*, bit7 is RGB1W_CMD_RAM
// 0x41: start temperature convert
// 0x42: get temperature data
// 0x43: start temperature convert then get data
#define CMD_T_START 0x41 // start temperature convert
#define CMD_T_GET 0x42 // get temperature data
#define CMD_T_STA_GET 0x43 // start temperature convert then get data
#define RGB1W_ERR_OK 0 // error code for success
#define RGB1W_ERR_CMD 1 // error code for command error
#define RGB1W_ERR_PARA 2 // error code for parameter error
#define RGB1W_ERR_OUTH 4 // error code for pin high at the end
#define RGB1W_ERR_PINH 6 // error code for pin high at the start
extern __IO uint8_t stat;
externconstunsignedchar PIOC_1W_CODE[] __attribute__((aligned (4)));
voidPIOC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
/*
R G B
红: 255 0 0
黄: 255 255 0
绿: 0 255 0
青: 0 255 255
蓝: 0 0 255
紫: 255 0 255
白: 255 255 255
*/
typedefunion_rgbPixelBuffer
{
struct
{
uint8_tPixelBuffer1[PIXEL_NUM1][GRB/8];
uint8_tPixelBuffer2[PIXEL_NUM2][GRB/8];
uint8_tPixelBuffer3[PIXEL_NUM3][GRB/8];
uint8_tPixelBuffer4[PIXEL_NUM4][GRB/8];
}buff;
uint8_tBuffer[PIXEL_NUM][GRB/8];
uint8_tAll_Buffer[PIXEL_NUM*(GRB/8)];
}RGB_PixelBuffer;
externRGB_PixelBufferRGB_Buffer;
voidRGB1W_SendSFR( uint16_ttotal_bytes, uint8_t *p_source_addr ); //SFR mode for 1~32 bytes data
voidRGB1W_SendRAM( uint16_ttotal_bytes, uint8_t *p_source_addr ); //RAM mode for 1~3072 bytes data
uint8_tRGB1W_SendSFR_Wait( uint16_ttotal_bytes, uint8_t *p_source_addr ); //SFR mode for 1~32 bytes data
uint8_tRGB1W_SendRAM_Wait( uint16_ttotal_bytes, uint8_t *p_source_addr ); //RAM mode for 1~3072 bytes data
voidRGB1W_Halt( void ); //halt/sleep PIOC
voidWS281x_Init(void);
voidWS281x_CloseAll(void);
uint32_tWS281x_Color(uint8_t red, uint8_t green, uint8_t blue);
voidWS281x_SetPixelColor(uint16_t n, uint32_tGRBColor);
voidWS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue);
voidWS281x_Show(void);
voidWS281x_RainbowCycle(uint8_t wait);
voidWS281x_TheaterChase(uint32_t c, uint8_t wait);
voidWS281x_ColorWipe(uint32_t c, uint8_t wait);
voidWS281x_Rainbow(uint8_t wait);
voidWS281x_TheaterChaseRainbow(uint8_t wait);
voidRGB_DEBUG_TEST(void);
#endif
#endif/* WS2812_PIOC_WS2812_H_ */
PIOC_WS2812.c
/*
* PIOC_WS2812.c
*
* Created on: 2023年9月10日
* Author: hasee
*/
#include"PIOC_WS2812.h"
#include<string.h>
#include"system_ch32x035.h"
RGB_PixelBufferRGB_Buffer;
__IO uint8_t stat;
voidPIOC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
/*********************************************************************
* @fnPIOC_IRQHandler
*
* @brief This function handles PIOC exception.
*
* @return none
*/
voidPIOC_IRQHandler( void )
{
// uint8_t stat;
stat = PIOC->D8_CTRL_RD;//auto remove interrupt request after reading
// if ( stat == RGB1W_ERR_OK ) printf("1-wire finished\r\n");
// else printf("1-wire error %02x\r\n", stat);
// temper = PIOC->D16_DATA_REG0_1;//for DS1820 only
}
constunsignedchar PIOC_1W_CODE[] ={
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x16,0x60,0x00,0x00,0x00,0x00,0x00,0x00, /* .........`...... */
0x00,0x00,0x0A,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ...p............ */
0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x1C,0x5E,0x1C,0x47, /* ......0......^.G */
0x1C,0x5D,0x16,0x60,0x1C,0x47,0x1E,0x02,0x09,0x10,0x31,0xC1,0x3E,0xC2,0x34,0xC3, /* .].`.G....1.>.4. */
0x2F,0x80,0x20,0x2F,0x41,0x38,0x87,0x2F,0x2F,0x38,0xFC,0x2F,0x93,0x38,0x00,0x00, /* ....A8...8...8.. */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x28, /* ...............( */
0x8E,0x60,0x00,0x00,0x2C,0x71,0x8E,0x60,0x00,0x00,0x2C,0x71,0x8E,0x30,0x4B,0x24, /* .`..,q.`..,q.0K$ */
0x28,0x22,0xE9,0x70,0x04,0x15,0x39,0x30,0x09,0x15,0x38,0x30,0x00,0x00,0x38,0x71, /* (".p..90..80..8q */
0x8E,0x60,0x00,0x00,0x08,0x01,0x0B,0x40,0x0A,0x48,0x0C,0x01,0x06,0x28,0x0B,0x54, /* .`.....@.H...(.T */
0x8E,0x60,0x09,0x02,0x04,0x10,0x20,0x24,0x00,0x00,0x0B,0x48,0x0B,0x70,0x01,0x5F, /* .`.....$...H.p._ */
0x0B,0x40,0x08,0x70,0x0B,0x40,0x0A,0x70,0x0B,0x48,0x0B,0x70,0x01,0x5E,0x0B,0x40, /* .@.p.@.p.H.p.^.@ */
0x08,0x70,0x0B,0x40,0x0A,0x70,0x0B,0x48,0x0B,0x70,0x01,0x5D,0x0B,0x40,0x08,0x70, /* .p.@.p.H.p.].@.p */
0x0B,0x40,0x0A,0x70,0x0B,0x48,0x0B,0x70,0x01,0x5C,0x0B,0x40,0x08,0x70,0x0B,0x40, /* .@.p.H.p.\.@.p.@ */
0x0A,0x70,0x0B,0x48,0x0B,0x70,0x01,0x5B,0x0B,0x40,0x08,0x70,0x0B,0x40,0x0A,0x70, /* .p.H.p.[.@.p.@.p */
0x0B,0x48,0x0B,0x70,0x01,0x5A,0x0B,0x40,0x08,0x70,0x0B,0x40,0x0A,0x70,0x0B,0x48, /* .H.p.Z.@.p.@.p.H */
0x0B,0x70,0x01,0x59,0x0B,0x40,0x08,0x70,0x0B,0x40,0x0A,0x70,0x0B,0x48,0x0B,0x70, /* .p.Y.@.p.@.p.H.p */
0x01,0x58,0x0B,0x40,0x09,0x70,0x01,0x02,0x0B,0x40,0x0D,0x70,0x04,0x15,0x4D,0x30, /* .X.@.p...@.p..M0 */
0x1E,0x28,0xEE,0x70,0xEE,0x70,0x04,0x00,0x0B,0x54,0x04,0x28,0x1D,0x10,0x1C,0x4F, /* .(.p.p...T.(...O */
0x16,0x60,0x02,0x28,0x8E,0x60,0x00,0x00,0x08,0x01,0x0B,0x40,0x80,0x28,0x0C,0x10, /* .`.(.`.....@.(.. */
0x20,0x02,0x21,0x0A,0x91,0x34,0x0A,0x48,0x09,0x47,0x3E,0x01,0x04,0x01,0x06,0x28, /* ..!..4.H.G>....( */
0x0B,0x54,0x8E,0x60,0x02,0x28,0x3F,0x10,0x18,0x00,0x1F,0x10,0x80,0x29,0x09,0x0A, /* .T.`.(?......).. */
0x00,0x00,0x08,0x10,0x13,0x00,0xAE,0x00,0x13,0x00,0xAD,0x00,0x13,0x00,0xAC,0x00, /* ................ */
0x13,0x00,0xAB,0x00,0x13,0x00,0xAA,0x00,0x13,0x00,0x20,0x15,0x20,0x04,0x03,0x52, /* ...............R */
0x21,0x15,0xA9,0x00,0x13,0x00,0x20,0x02,0x21,0x0A,0xE3,0x34,0xA8,0x00,0x13,0x00, /* !.......!..4.... */
0x04,0x02,0x1F,0x10,0xAF,0x00,0x13,0x00,0xAE,0x00,0x13,0x00,0xAD,0x00,0x13,0x00, /* ................ */
0xAC,0x00,0x13,0x00,0x20,0x15,0x20,0x04,0x03,0x52,0x21,0x15,0xAB,0x00,0x13,0x00, /* .........R!..... */
0x3E,0x14,0x03,0x52,0x3F,0x14,0xAA,0x00,0x13,0x00,0x3E,0x02,0x04,0x10,0xA9,0x00, /* >..R?.....>..... */
0x13,0x00,0x20,0x02,0x21,0x0A,0xE3,0x34,0x3F,0x02,0xA8,0x00,0x13,0x00,0x18,0x00, /* ....!..4?....... */
0x1F,0x10,0xAF,0x00,0xAA,0x60,0xA8,0x00,0x12,0x00,0x13,0x00,0x12,0x00,0x08,0x01, /* .....`.......... */
0x88,0x60,0xFA,0x28,0xEE,0x60,0x41,0x28,0xEE,0x60,0x02,0x28,0x0D,0x70,0x00,0x00, /* .`.(.`A(.`.(.p.. */
0x0D,0x70,0x00,0x00,0x0D,0x70,0x00,0x00,0x0D,0x70,0x00,0x00,0xFF,0x2C,0x00,0x00, /* .p...p...p...,.. */
0xEE,0x30,0x30,0x00,0x0A,0x40,0x08,0x01,0x0C,0x01,0x05,0x28,0xEE,0x70,0x0B,0x40, /* .00..@.....(.p.@ */
0x0A,0x48,0xE9,0x70,0xE9,0x70,0x0A,0x40,0xEB,0x70,0x09,0x01,0x0B,0x54,0x04,0x24, /* .H.p.p.@.p...T.$ */
0xE9,0x70,0x09,0x02,0x30,0x00,0x09,0x10,0x08,0x22,0x0B,0x40,0x0A,0x48,0xED,0x70, /* .p..0....".@.H.p */
0x09,0x50,0x0A,0x40,0xEB,0x70,0x0A,0x40,0x05,0x28,0x09,0x58,0xEE,0x70,0x09,0x1F, /* .P.@.p.@.(.X.p.. */
0x04,0x15,0x0D,0x31,0x30,0x00,0x08,0x22,0x0B,0x40,0x0A,0x48,0xED,0x70,0x0A,0x40, /* ...10..".@.H.p.@ */
0x0A,0x28,0xEE,0x70,0x09,0x1F,0x09,0x47,0x0B,0x54,0x09,0x4F,0x37,0x28,0xEE,0x70, /* .(.p...G.T.O7(.p */
0x04,0x15,0x1C,0x31,0x09,0x02,0x30,0x00,0xFA,0x70,0x37,0x31,0xCC,0x28,0x0B,0x71, /* ...1..0..p71.(.q */
0x44,0x28,0x0B,0x71,0x1C,0x5C,0x36,0x61,0x0B,0x48,0x0A,0x48,0x04,0x00,0x30,0x00, /* D(.q.\6a.H.H..0. */
0xFA,0x70,0x43,0x31,0xCC,0x28,0x0B,0x71,0xBE,0x28,0x0B,0x71,0x1B,0x71,0x20,0x10, /* .pC1.(.q.(.q.q.. */
0x1B,0x71,0x21,0x10,0xFA,0x70,0x30,0x00,0x00,0x00}; /* .q!..p0... */
voidRGB1W_Init ( void )
{
GPIO_InitTypeDefGPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOC, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_IO2W, ENABLE);
#if 1 //PC18
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);//禁用下载功能
GPIO_SetBits(GPIOC, GPIO_Pin_18);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_18;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
#else//PC7
GPIO_PinRemapConfig(GPIO_Remap_PIOC, ENABLE);
GPIO_SetBits(GPIOC, GPIO_Pin_7);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
#endif
// PIOC->D8_SYS_CFG = 0;//停止
PIOC->D8_SYS_CFG = RB_MST_RESET | RB_MST_IO_EN0; //复位PIOC并且使能IO0
memcpy( (uint8_t *)(PIOC_SRAM_BASE), PIOC_1W_CODE, sizeof( PIOC_1W_CODE ) );// PIOC装载代码
#ifUSE_24MHZHSI
RGB1W_COMMAND = RGB1W_CYC_24M | RGB1W_CMD_RAM;// set bit cycle and PIOC start send
#else// default is 48MHz
RGB1W_COMMAND = RGB1W_CYC_48M | RGB1W_CMD_RAM;// set bit cycle and PIOC start send
#endif
NVIC_EnableIRQ(PIOC_IRQn);//enable interrupt
NVIC_SetPriority(PIOC_IRQn,0xf0);
}
/*********************************************************************
* @fn RGB1W_SendSFR
*
* @brief SFR mode for 1~32 bytes data.
*
* @paramtotal_bytes - total data number(byte).
* p_source_addr - data.
*
* @return none
*/
voidRGB1W_SendSFR( uint16_ttotal_bytes, uint8_t *p_source_addr ) {//SFR mode for 1~32 bytes data
// p_source_addr: point source data buffer start address, set NULL if copy into PIOC buffer already
if ( total_bytes > RGB1W_SFR_SIZE ) return;
PIOC->D8_SYS_CFG = RB_MST_RESET | RB_MST_IO_EN0;//clear&halt PIOC
PIOC->D8_SYS_CFG = RB_MST_CLK_GATE | RB_MST_IO_EN0;//run PIOC before write SFR
if ( p_source_addr ) memcpy( RGB1W_SFR_ADDR, p_source_addr, total_bytes );//copy source data to RGB1W SFR, @PIOC run
RGB1W_COMMAND = (uint8_t)total_bytes;// PIOC start send
}
/*********************************************************************
* @fn RGB1W_SendRAM
*
* @brief RAM mode for 1~3072 bytes data.
*
* @paramtotal_bytes - total data number(byte).
* p_source_addr - data.
*
* @return none
*/
voidRGB1W_SendRAM( uint16_ttotal_bytes, uint8_t *p_source_addr ) {//RAM mode for 1~3072 bytes data
// p_source_addr: point source data buffer start address, set NULL if copy into PIOC buffer already
if ( total_bytes > RGB1W_RAM_SIZE ) return;
PIOC->D8_SYS_CFG = RB_MST_RESET | RB_MST_IO_EN0;//clear&halt PIOC
if ( p_source_addr ) memcpy( RGB1W_RAM_ADDR, p_source_addr, total_bytes );//copy source data to PIOC SRAM, @PIOC halt
PIOC->D8_SYS_CFG = RB_MST_CLK_GATE | RB_MST_IO_EN0;//run PIOC after load data in SRAM
PIOC->D16_DATA_REG0_1 = total_bytes;// data size
#ifUSE_24MHZHSI
RGB1W_COMMAND = RGB1W_CYC_24M | RGB1W_CMD_RAM;// set bit cycle and PIOC start send
#else// default is 48MHz
RGB1W_COMMAND = RGB1W_CYC_48M | RGB1W_CMD_RAM;// set bit cycle and PIOC start send
#endif
}
/*********************************************************************
* @fn RGB1W_SendSFR_Wait
*
* @brief SFR mode for 1~3072 bytes data wait PIOC operate finish.
*
* @paramtotal_bytes - total data number(byte).
* p_source_addr - data.
*
* @return none
*/
uint8_tRGB1W_SendSFR_Wait( uint16_ttotal_bytes, uint8_t *p_source_addr ) {//SFR mode for 1~32 bytes data
// p_source_addr: point source data buffer start address, set NULL if copy into PIOC buffer already
if ( total_bytes == 0 || total_bytes > RGB1W_SFR_SIZE ) return( RGB1W_ERR_PARA );
RGB1W_SendSFR( total_bytes, p_source_addr );
while ( ( PIOC->D8_SYS_CFG & RB_INT_REQ ) == 0 );//wait, PIOC request interrupt after finish
return( PIOC->D8_CTRL_RD );//auto remove interrupt request after reading
}
/*********************************************************************
* @fn RGB1W_SendRAM_Wait
*
* @brief RAM mode for 1~3072 bytes data wait PIOC operate finish.
*
* @paramtotal_bytes - total data number(byte).
* p_source_addr - data.
*
* @return none
*/
uint8_tRGB1W_SendRAM_Wait( uint16_ttotal_bytes, uint8_t *p_source_addr ) {//RAM mode for 1~3072 bytes data
// p_source_addr: point source data buffer start address, set NULL if copy into PIOC buffer already
if ( total_bytes == 0 || total_bytes > RGB1W_RAM_SIZE ) return( RGB1W_ERR_PARA );
RGB1W_SendRAM( total_bytes, p_source_addr );
while ( ( PIOC->D8_SYS_CFG & RB_INT_REQ ) == 0 );//wait, PIOC request interrupt after finish
return( PIOC->D8_CTRL_RD );//auto remove interrupt request after reading
}
/*********************************************************************
* @fn RGB1W_Halt
*
* @brief halt/sleep PIOC .
*
* @return none
*/
voidRGB1W_Halt( void ) {//halt/sleep PIOC
PIOC->D8_SYS_CFG &= ~ RB_MST_CLK_GATE;
}
voidWS281x_Init(void)
{
RGB1W_Init( );
stat = 0x80; //free
WS281x_CloseAll();
}
#definetimer_to_run 1 // start by timer
voidWS281x_Show(void)
{
uint16_ttotal_bytes;
uint8_t* RGB_RAM;
total_bytes = sizeof(RGB_Buffer.All_Buffer);
stat = 0x80;//free
if ( stat != 0xFF ) {//RGB1W completed
if ( stat < 0x80 ) {//got status in interrupt
if ( stat == RGB1W_ERR_OK ) printf("1-wire finished\r\n")/**/;
elseprintf("1-wire error %02x\r\n", stat)/* */;
stat = 0x80;//free
}
if ( stat == 0x80 && total_bytes && timer_to_run ) {//RAM mode for 1~3072 bytes data
stat = 0xFF;//wait
RGB_RAM = RGB_Buffer.All_Buffer;
RGB1W_SendRAM( total_bytes, RGB_RAM );
total_bytes = 0;
}
}
if ( PIOC->D8_SYS_CFG & RB_INT_REQ ) {//query if disable interrupt
stat = PIOC->D8_CTRL_RD;//auto remove interrupt request after reading
}
}
//配置完成之后便可以构思底层控制函数了,为了方便多个LED灯珠的可控制首先要定义一个缓冲区pixelBuffer[PIXEL_NUM][24]\
通过设定颜色将数据填入缓冲区再通过更新函数将数据传入到LED灯珠上。
//关闭所有灯珠
voidWS281x_CloseAll(void)
{
uint16_ti;
for(i = 0; i < PIXEL_NUM*(GRB/8); ++i)
{
RGB_Buffer.All_Buffer = 0X00;
}
WS281x_Show();
}
uint32_tWS281x_Color(uint8_t red, uint8_t green, uint8_t blue)
{
return green << 16 | red << 8 | blue;
}
voidWS281x_SetPixelColor(uint16_t n, uint32_tGRBColor)
{
RGB_Buffer.Buffer[n][0] = (GRBColor>>16)&0X000000FF;//绿色值
RGB_Buffer.Buffer[n][1] = (GRBColor>>8)&0X000000FF;//红色值
RGB_Buffer.Buffer[n][2] = (GRBColor)&0X000000FF;//蓝色值
}
voidWS281x_SetPixelRGB(uint16_t n ,uint8_t red, uint8_t green, uint8_t blue)
{
RGB_Buffer.Buffer[n][0] = green;//绿色值
RGB_Buffer.Buffer[n][1] = red;//红色值
RGB_Buffer.Buffer[n][2] = blue;//蓝色值
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_tWS281x_Wheel(uint8_twheelPos) {
wheelPos = 255 - wheelPos;
if(wheelPos < 85) {
return WS281x_Color(255 - wheelPos * 3, 0, wheelPos * 3);
}
if(wheelPos < 170) {
wheelPos -= 85;
return WS281x_Color(0, wheelPos * 3, 255 - wheelPos * 3);
}
wheelPos -= 170;
return WS281x_Color(wheelPos * 3, 255 - wheelPos * 3, 0);
}
// Fill the dots one after the other with a color
voidWS281x_ColorWipe(uint32_t c, uint8_t wait) {
for(uint16_ti=0; i<PIXEL_NUM; i++) {
WS281x_SetPixelColor(i, c);
WS281x_Show();
Delay_Ms(wait);
}
}
voidWS281x_Rainbow(uint8_t wait) {
uint16_ti, j;
for(j=0; j<256; j++) {
for(i=0; i<PIXEL_NUM; i++) {
WS281x_SetPixelColor(i, WS281x_Wheel((i+j) & 255));
}
WS281x_Show();
Delay_Ms(wait);
}
}
// Slightly different, this makes the rainbow equally distributed throughout
voidWS281x_RainbowCycle(uint8_t wait) {
uint16_ti, j;
for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
for(i=0; i< PIXEL_NUM; i++) {
WS281x_SetPixelColor(i,WS281x_Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
}
WS281x_Show();
Delay_Ms(wait);
}
}
//Theatre-style crawling lights.
voidWS281x_TheaterChase(uint32_t c, uint8_t wait) {
for (int j=0; j<10; j++) { //do 10 cycles of chasing
for (int q=0; q < 3; q++) {
for (uint16_ti=0; i < PIXEL_NUM; i=i+3) {
WS281x_SetPixelColor(i+q, c); //turn every third pixel on
}
WS281x_Show();
Delay_Ms(wait);
for (uint16_ti=0; i < PIXEL_NUM; i=i+3) {
WS281x_SetPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
//Theatre-style crawling lights with rainbow effect
voidWS281x_TheaterChaseRainbow(uint8_t wait) {
for (int j=0; j < 256; j++) { // cycle all 256 colors in the wheel
for (int q=0; q < 3; q++) {
for (uint16_ti=0; i < PIXEL_NUM; i=i+3) {
WS281x_SetPixelColor(i+q, WS281x_Wheel( (i+j) % 255)); //turn every third pixel on
}
WS281x_Show();
Delay_Ms(wait);
for (uint16_ti=0; i < PIXEL_NUM; i=i+3) {
WS281x_SetPixelColor(i+q, 0); //turn every third pixel off
}
}
}
}
// Slightly different, this makes the rainbow equally distributed throughout
voidWS2812_RainbowRotate(uint16_t wait) {
uint16_ti, j;
for (j = 0; j < 256 * 5; j++) { // 5 cycles of all colors on wheel
for (i = 0; i < PIXEL_NUM; i++) {
WS281x_SetPixelColor(i, WS281x_Wheel(((i * 256 / PIXEL_NUM) + j) & 255));
}
WS281x_Show();
Delay_Ms(wait);
}
}
//hsv和rgb互相转
floatretmax(float a, float b, float c) //求最大值
{
float max = 0;
max = a;
if (max < b)
max = b;
if (max < c)
max = c;
return max;
}
floatretmin(float a, float b, float c) //求最小值
{
float min = 0;
min = a;
if (min > b)
min = b;
if (min > c)
min = c;
return min;
}
//R,G,B参数传入范围(0~100)
//转换结果h(0~360),s(0~100),v(0~100)
voidrgb_to_hsv(uint16_t *H, uint16_t *S, uint16_t *V, uint8_t r, uint8_t g, uint8_t b) {
float max = 0, min = 0;
float R = (float)r;
float G = (float)g;
float B = (float)b;
float h, s, v;
R = R / 255.0;
G = G / 255.0;
B = B / 255.0;
max = retmax(R, G, B);
min = retmin(R, G, B);
v = max;
if (max == 0)
s = 0;
else
s = 1 - (min / max);
if (max == min)
h = 0;
elseif (max == R && G >= B)
h = 60 * ((G - B) / (max - min));
elseif (max == R && G < B)
h = 60 * ((G - B) / (max - min)) + 360;
elseif (max == G)
h = 60 * ((B - R) / (max - min)) + 120;
elseif (max == B)
h = 60 * ((R - G) / (max - min)) + 240;
v = v * 100;
s = s * 100;
*H = (int) h;
*S = (int) s;
*V = (int) v;
}
//参数入参范围h(0~360),s(0~100),v(0~100),这里要注意,要把s,v缩放到0~1之间
//转换结果R(0~100),G(0~100),B(0~100),如需转换到0~255,只需把后面的乘100改成乘255
voidhsv_to_rgb(int h, int s, int v, uint8_t *r, uint8_t *g, uint8_t *b) {
float C = 0, X = 0, Y = 0, Z = 0;
inti = 0;
float H = (float) (h);
float S = (float) (s) / 100.0;
float V = (float) (v) / 100.0;
float R, G, B;
if (S == 0)
R = G = B = V;
else {
H = H / 60;
i = (int) H;
C = H - i;
X = V * (1 - S);
Y = V * (1 - S * C);
Z = V * (1 - S * (1 - C));
switch (i) {
case 0:
R = V;
G = Z;
B = X;
break;
case 1:
R = Y;
G = V;
B = X;
break;
case 2:
R = X;
G = V;
B = Z;
break;
case 3:
R = X;
G = Y;
B = V;
break;
case 4:
R = Z;
G = X;
B = V;
break;
case 5:
R = V;
G = X;
B = Y;
break;
}
}
R = R * 255;
G = G * 255;
B = B * 255;
*r = (int) R;
*g = (int) G;
*b = (int) B;
}
voidRGB_DEBUG_TEST(void)
{
inti,j;
for(i=0;i<3;i++)
{
for(j=0;j<8;j++)
{
if(i==0)
{
WS281x_ColorWipe(WS281x_Color(255, 0, 0), 2); // Red
}
elseif(i==1)
{
WS281x_ColorWipe(WS281x_Color(0, 255, 0), 2); // Green
}
else
{
WS281x_ColorWipe(WS281x_Color(0, 0, 0), 2); // Blue
}
}
}
WS2812_RainbowRotate(2);
// Some example procedures showing how to display to the pixels:
WS281x_ColorWipe(WS281x_Color(255, 0, 0), 2); // Red
WS281x_ColorWipe(WS281x_Color(0, 255, 0), 2); // Green
WS281x_ColorWipe(WS281x_Color(0, 0, 255), 2); // Blue
WS281x_TheaterChase(WS281x_Color(127, 127, 127), 2); // White
WS281x_TheaterChase(WS281x_Color(127, 0, 0), 2); // Red
WS281x_TheaterChase(WS281x_Color(0, 0, 127), 2); // Blue
WS281x_Rainbow(2);
WS281x_RainbowCycle(2);
WS281x_TheaterChaseRainbow(2);
}
Main.c
#include"PIOC_WS2812.h"
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
intmain(void)
{
Delay_Init();
Delay_Ms(1000);
Delay_Ms(1000);
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
printf("PIOC WS2812 Test\r\n");
WS281x_Init( );
stat = 0x80; //free
while ( 1 )
{
WS281x_SetPixelRGB(0,0,255,0);//第一颗灯珠显示蓝色
WS281x_Show();//显示
Delay_Ms(200);
RGB_DEBUG_TEST();//测试彩虹效果
WS281x_ColorWipe(WS281x_Color(255, 0, 0), 2); // 全部显示红色
Delay_Ms(1000);
WS281x_ColorWipe(WS281x_Color(0, 255, 0), 2); // 全部显示绿色
Delay_Ms(1000);
WS281x_ColorWipe(WS281x_Color(0, 0, 255), 2); // 全部显示蓝色
Delay_Ms(1000);
}
}
四、展示
BiliBili链接:【CH32X035C8T6的PIOC外设驱动WS2812。-哔哩哔哩】 https://b23.tv/2PHiz5b
五、心得与体会
总的来看CH32X035的外设来看还是很丰富的,在PIOC这个外设来看,还是很有优势的,之前驱动WS2812的方案主要是PWM+DMA和SPI+DMA的。在今看来还多了一个PIOC驱动。 其次就是WCH的芯片开发库函数基本的都是兼容的,不同系列的芯片相互移植还是非常快的。 谢谢大家,如有不当之处,还望不吝赐教。 |