Funpack5-2:基于PIC32CM LS00 Curiosity Nano实现PWM输出与RGB灯控制
该项目使用了PIC32CM LS00 Curiosity Nano,实现了PWM输出控制RGB灯颜色亮度的设计,它的主要功能为:使用 EV41C56A 芯片的 PWM 输出功能,用3路PWM分别控制外部的 RGB 三色灯的R/G/B通道。实现至少3种不同颜色的静态显示。。
标签
Funpack活动
PWM
PIC32CM LS00
流水源
更新2026-06-22
37

一、项目介绍

本次任务使用 EV41C56A 开发评估板MCU实现PWM 输出功能,并控制板子外部的 RGB 三色灯点亮不同颜色。要求使用3路PWM分别控制LED灯的 R/G/B通道。实现至少3种不同颜色的静态显示,PWM频率与分辨率设置合理,低亮度时无明显闪烁。

1.1、硬件介绍

Microchip EV41C56A 开发板简介:

PIC32CM LS00 Curiosity Nano+ Touch Evaluation Kit 是一款专为评估 PIC32CM5164LS00048 微控制器而设计的硬件平台。该套件基于安全且超低功耗的 ARM® Cortex®-M23 内核,集成了丰富的安全功能和触摸控制能力这款评估套件将安全启动、TrustZone® 技术、加密加速器与增强型触摸控制完美融合,是开发安全触摸应用的理想选择。

关键参数

  • PIC32CM5164LS00048 ARM® Cortex®-M23 内核微控制器
  • 512KB Flash 存储器,超低功耗设计
  • 安全启动(Secure Boot)+ ARM TrustZone® 技术
  • 集成加密加速器,硬件安全保障
  • 增强型外设触摸控制器(PTC),支持电容式触摸
  • 智能模拟功能:运算放大器、ADC、DAC、模拟比较器
  • 板载调试器,支持 MPLAB X IDE 开发环境
  • 电压范围 1.7V-3.6V,MIC5353 稳压器支持最大 500mA
  • 兼容 Curiosity Nano Base for Click boards™ 扩展
  • 支持 mikroBUS™、Xplained Pro 接口扩展

1.2、任务设计方案

PIC32CM5164LS00有三个16位定时器/计数器(TC)具有高度可配置性,能够作为16位、8位或组合的32位定时器运行,并带有比较/捕获通道。针对高级电机控制和数字电源转换,最多有三个24位控制定时器/计数器(TCC)和一个16位TCC。这些模块支持故障检测、抖动、死区插入和模式生成等功能。总的来说,系统可生成数量可观的PWM输出:每个24位TCC最多可提供8路,另一个可提供4路,每个16位TC可提供2路,为多轴控制或复杂照明模式提供了充足的资源。


本次使用MCU的TC定时器实现PWM输出。使用引脚PA14(TC0-CH0),PA15(TC0-CH1),PA20(TC1-CH0)连接RGB LED灯的引脚。通过定时器输出PWM脉冲调节LED灯亮度,实现不同颜色显示。

二、任务实现

下面实现软件编程代码实现PWM输出,使用KEIL开发环境。需要安装MCU的pack开发包。

2.1、软件流程

软件代码控制流程如下,通过更新不同的PWM占空比实现不同的RGB颜色显示。

image.png

2.2、任务实现过程

本次通过keil开发环境,首先下载PIC32CM LS00 的pack开发包。

image.png

然后新建基于PIC32CM5164LS00048工程。

image.png

再就是根据MCU寄存器文档实现外设驱动。

2.3、任务实现代码

下面展示外设驱动代码:

首先是时钟设置,这里设置系统时钟频率为内部16MHz。

//-----------------------------------------------------------------------------
// Clock Configuration
//-----------------------------------------------------------------------------
static void configure_clock(void)
{
SUPC_REGS->SUPC_VREGPLL |= SUPC_VREGPLL_ENABLE_Msk;
// 1. 配置 OSC16M 为 16 MHz
OSCCTRL_REGS->OSCCTRL_OSC16MCTRL = (1 << 1) /* ENABLE */ | (3 << 2) /* FSEL=16M */;
// 等待 OSC16M 稳定
while((OSCCTRL_REGS->OSCCTRL_STATUS & OSCCTRL_STATUS_OSC16MRDY_Msk) == 0);
// 2. 配置 GCLK Generator 1: 输入 OSC16M, 8分频 -> 2 MHz
GCLK_REGS->GCLK_GENCTRL[1] = (1 << 8) /* GENEN */ | (5 << 0) /* SRC=OSC16M */ |(8 << 16);
// 等待同步
while(GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL1_Msk);

GCLK_REGS->GCLK_GENCTRL[2] = (1 << 8) /* GENEN */ | (3 << 0) /* SRC=OSCULP32K */;
// 等待同步
while(GCLK_REGS->GCLK_SYNCBUSY & GCLK_SYNCBUSY_GENCTRL2_Msk);

// 3. 将 GCLK Generator 1 连接到 GCLK_FDPLL96M 通道
GCLK_REGS->GCLK_PCHCTRL[0] = (1 << 6) /* CHEN */ | (1 << 0) /* GEN=1 */; // 注意 PCHCTRL 索引 0 对应 GCLK_FDPLL96M
// 同样配置 GCLK_FDPLL96M_32K 通道,假设使用 OSCULP32K 作为源(需确认 PCHCTRL 索引 1)
GCLK_REGS->GCLK_PCHCTRL[1] = (1 << 6) | (2 << 0); // 选择一个合适的 generator,比如 Generator 2 输出 OSCULP32K

// 4. 配置 PLL 参数
OSCCTRL_REGS->OSCCTRL_DPLLCTRLB = OSCCTRL_DPLLCTRLB_REFCLK_GCLK | OSCCTRL_DPLLCTRLB_LTIME_8MS_Val | OSCCTRL_DPLLCTRLB_FILTER_LBFILT_Val; // GCLK_FDPLL96M
OSCCTRL_REGS->OSCCTRL_DPLLRATIO = OSCCTRL_DPLLRATIO_LDR(47);
while(OSCCTRL_REGS->OSCCTRL_DPLLSYNCBUSY & OSCCTRL_DPLLSYNCBUSY_DPLLRATIO_Msk);
// 使能 PLL
OSCCTRL_REGS->OSCCTRL_DPLLCTRLA = OSCCTRL_DPLLCTRLA_ENABLE_Msk;
while(OSCCTRL_REGS->OSCCTRL_DPLLSYNCBUSY & OSCCTRL_DPLLSYNCBUSY_ENABLE_Msk);
while(!(OSCCTRL_REGS->OSCCTRL_DPLLSTATUS & OSCCTRL_DPLLSTATUS_LOCK_Msk));

// // 5. 增加 Flash Wait States(根据 96MHz 查表)
// NVMCTRL_REGS->NVMCTRL_CTRLB = NVMCTRL_CTRLB_RWS(3); // 例如 3 wait states

// // 6. 提升性能等级到 PL2
// PM->PLCFG.bit.PLSEL = 2;
// while(PM->INTFLAG.bit.PLRDY == 0); // 等待电压稳定

// // 7. 切换主时钟到 PLL (GENCTRL0)
// GCLK->GENCTRL0.reg = (1 << 8) /* GENEN */ | (8 << 0) /* SRC=FDPLL96M */ /* 不分频 */;
// while(GCLK->SYNCBUSY.bit.GENCTRL0);

SystemCoreClock = 16000000;

// // SERCOM1 clock (USART)
// GCLK_SetGeneratorSource(GCLK_GENERATOR_1, GCLK_SRC_OSCHF);
// GCLK_SetDivisionParameters(GCLK_GENERATOR_1, 6, GCLK_DIVISION_INTEGER, 0);
// GCLK_GeneratorEnable(GCLK_GENERATOR_1);
// GCLK_SetPeripheralChannelGenSrc(GCLK_PCHCCTRL_8, GCLK_GENERATOR_1);
// GCLK_EnablePeripheralChannel(GCLK_PCHCCTRL_8);

// MCLK_EnableAPBCClock(MCLK_APBC_SERCOM1);
}

再就是IO端口功能配置:

#define GPIO_PINS_PER_GROUP    (32U)
#define GPIO_MAX_PINS (GPIO_PINS_PER_GROUP * (uint32_t)PORT_GROUP_NUMBER)

#define PIN_IS_AVAILABLE(pin) ((uint32_t)(pin) < (uint32_t)GPIO_MAX_PINS)
#define PIN_MASK(pin) (1UL << ((uint32_t)(pin) % GPIO_PINS_PER_GROUP))
#define PIN_GROUP(pin) ((uint32_t)(pin) / GPIO_PINS_PER_GROUP)
#define PIN_INDEX(pin) ((uint32_t)(pin) % GPIO_PINS_PER_GROUP)

int pinPeripheral( uint32_t ulPin, EPioType ulPeripheral )
{
uint32_t mux_reg_n;
if(!PIN_IS_AVAILABLE(ulPin)) return -1;
switch ( ulPeripheral )
{
case PIO_DIGITAL:
case PIO_INPUT:
case PIO_INPUT_PULLUP:
case PIO_OUTPUT:
// Disable peripheral muxing, done in pinMode
// Configure pin mode, if requested
if ( ulPeripheral == PIO_INPUT )
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_DIRCLR = PIN_MASK(ulPin);
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] = 0x02U;
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTSET = PIN_MASK(ulPin);
}else
{
if ( ulPeripheral == PIO_INPUT_PULLUP )
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_DIRCLR = PIN_MASK(ulPin);
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] = 0x06U;
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTSET = PIN_MASK(ulPin);
}
else
{
if ( ulPeripheral == PIO_OUTPUT )
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_DIRSET = PIN_MASK(ulPin);
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] = 0x44U;
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTSET = PIN_MASK(ulPin);
}else
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_DIRSET = PIN_MASK(ulPin);
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] = 0x06U;
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTSET = PIN_MASK(ulPin);
}
}
}
break ;

case PIO_EXTINT:
case PIO_ANALOG:
case PIO_SERCOM:
case PIO_SERCOM_ALT:
case PIO_TIMER:
case PIO_TIMER_ALT:

case PIO_COM:
case PIO_AC_CLK:
case PIO_CCL:
case PIO_TCC:
case PIO_I2S:

mux_reg_n = PIN_INDEX(ulPin) >> 1;
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] |= PORT_PINCFG_PMUXEN_Msk;
if(((PIN_INDEX(ulPin)) & 0x1U) == 0U)
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PMUX[mux_reg_n] = (PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PMUX[mux_reg_n] & ~PORT_PMUX_PMUXE_Msk)
| ((uint8_t)ulPeripheral << PORT_PMUX_PMUXE_Pos);
}
else
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PMUX[mux_reg_n] = (PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PMUX[mux_reg_n] & ~PORT_PMUX_PMUXO_Msk)
| ((uint8_t)ulPeripheral << PORT_PMUX_PMUXO_Pos);
}
break ;
case PIO_NOT_A_PIN:
return -1 ;
break ;
}
return 0 ;
}

void digitalWrite( uint32_t ulPin, int32_t ulVal )
{
// Handle the case the pin isn't usable as PIO
if(!PIN_IS_AVAILABLE(ulPin)) return ;

if ((PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_DIRSET & PIN_MASK(ulPin)) == 0 )
{
// the pin is not an output, disable pull-up if val is LOW, otherwise enable pull-up
if(ulVal == 0) PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] &= ~0x04;
else if(ulVal > 0) PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_PINCFG[PIN_INDEX(ulPin)] |= 0x04;
}

if( ulVal == 0 )
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTCLR = PIN_MASK(ulPin);
}else if( ulVal > 0 )
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTSET = PIN_MASK(ulPin);
}else
{
PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_OUTTGL = PIN_MASK(ulPin);
}
return ;
}

int digitalRead( uint32_t ulPin )
{
// Handle the case the pin isn't usable as PIO
if(!PIN_IS_AVAILABLE(ulPin)) return 0;

if ( (PORT_SEC_REGS->GROUP[PIN_GROUP(ulPin)].PORT_IN & PIN_MASK(ulPin)) != 0 )
{
return 1;
}
return 0 ;
}

最后就是定时器PWM输出配置:

// -- Configure TC
void ConfigurePWM(tc_registers_t* TCx,uint8_t tcChannel,uint32_t value)
{
GCLK_REGS->GCLK_PCHCTRL[23] = GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN_Msk; //TC0\TC1 use clock generator 0
GCLK_REGS->GCLK_PCHCTRL[24] = GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN_Msk; //TC2 use clock generator 0

//reset
// TCx->COUNT8.TC_CTRLA |= TC_CTRLA_SWRST_Msk;
// while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_SWRST_Msk);

// Disable TCx
TCx->COUNT8.TC_CTRLA &= ~TC_CTRLA_ENABLE_Msk;
while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_ENABLE_Msk);
// Set Timer counter Mode to 8 bits, normal PWM, prescaler 1/256
TCx->COUNT8.TC_CTRLA = TC_CTRLA_MODE_COUNT8 | TC_CTRLA_PRESCALER_DIV1;
TCx->COUNT8.TC_WAVE = TC_WAVE_WAVEGEN_NPWM;
while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_CC0_Msk);
// Set the initial value
TCx->COUNT8.TC_CC[tcChannel] = (uint8_t) value;
while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_CC0_Msk);
// Set PER to maximum counter value (resolution : 0xFF)
TCx->COUNT8.TC_PER = 0xff; //f=2000KHz / 200 = 10KHz
while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_PER_Msk);
TCx->COUNT8.TC_DRVCTRL = 0X03;
// Enable TCx
TCx->COUNT8.TC_CTRLA |= TC_CTRLA_ENABLE_Msk;
while (TCx->COUNT8.TC_SYNCBUSY & TC_SYNCBUSY_ENABLE_Msk);

}

main主函数代码如下,通过按键可以改变PWM占空比输出。

int main(void)
{
uint8_t ledlight;

// System Initialization
configure_clock();
SysTick_Config(SystemCoreClock / 1000);
//PORT_SEC_REGS->GROUP[0].PORT_DIRSET = 0x01ul<<15;

pinPeripheral(23,PIO_INPUT);


pinPeripheral(15,PIO_TIMER);
pinPeripheral(14,PIO_TIMER);
pinPeripheral(20,PIO_TIMER);

ConfigurePWM(TC0_REGS,1,50);
ConfigurePWM(TC0_REGS,0,50);
ConfigurePWM(TC1_REGS,0,50);
while(1)
{
delay_ms(30);
if(!digitalRead(23))
{
ledlight ++;
ConfigurePWM(TC0_REGS,1,Point2ColorTable[ledlight]>>16);
ConfigurePWM(TC0_REGS,0,Point2ColorTable[ledlight]>>8);
ConfigurePWM(TC1_REGS,0,Point2ColorTable[ledlight]);
}

//PORT_SEC_REGS->GROUP[0].PORT_OUTTGL = 0x01ul<<15;
//digitalWrite(15,-1);
}
}

最终编译下载就可以实现PWM输出控制LED颜色亮度了。

三、功能展示

下面展示实物连接图:

image.png

image.png

image.png

image.png

四、总结

非常高兴能参加这个活动。本次活动主要学习了MicroChip的PIC32CM LS00系列MCU的keil开发环境的搭建和应用,以及PIC32CM LS00系列的GPIO,定时器PWM等外设的应用。







附件下载
pic32_demo.zip
代码
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号