本帖最后由 hejun96 于 2021-9-1 16:10 编辑
#申请原创# @21小跑堂
最近ST家的涨价潮和缺货潮下,国产选型越来越火爆,最近使用的一款是CX32L003 封装是TSSOP-20,可替代STM8S003F3 20 pin对应的型号,其资源分布和STM8S003的对比如附图,但STM8S003和CX32L003的引脚资源分配不一样,而CX32L003还是ARM Cortex M0的核,所以只能使用CX32L003推出的HAL库或者是CX32L003的寄存器操作了,下面会介绍两种操作方式。
对于任意的一款单片机,首当其冲的就是时钟,时钟作为单片机的心脏为其提供"心跳",决定了定时器的时钟周期和UART波特率的分频等。以下是CX32L003的时钟(附图配有时钟树):
外部高速RC时钟HIRC(4MHz)(默认主频)
外部低速晶振时钟LXT(32.768KHZ) ->一般可作为RTC的时钟使用
内部低速RC时钟LIRC(38.4KHz与32.768KHz可配置)
外部高速时钟HXT(4MHz~24MHz)
因为平时多用内部时钟,这里我们用HAL库根据例程配置下内部高速时钟24MHz作主频(时钟源初始化):
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HIRC;
RCC_OscInitStruct.HIRCState = RCC_HIRC_ON;
RCC_OscInitStruct.HIRCCalibrationValue = RCC_HIRCCALIBRATION_24M;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HIRC;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APBCLKDivider = RCC_PCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct) != HAL_OK)
{
Error_Handler();
}
}
可以用GPIO初始化输出高低电平验证下,此处以LED为例 :
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] Configures LED GPIO.
* @param Led: Led to be configured.
* This parameter can be one of the following values:
* [url=home.php?mod=space&uid=2817080]@ARG[/url] LED1
*/
void BSP_LED_Init(Led_TypeDef Led)
{
GPIO_InitTypeDef gpioinitstruct={0};
/* Enable the GPIO_LED Clock */
LEDx_GPIO_CLK_ENABLE(Led);
/* Configure the GPIO_LED pin */
gpioinitstruct.Pin = LED_PIN[Led];
gpioinitstruct.Mode = GPIO_MODE_OUTPUT;
gpioinitstruct.OpenDrain = GPIO_PUSHPULL;
gpioinitstruct.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;
gpioinitstruct.SlewRate = GPIO_SLEW_RATE_HIGH;
gpioinitstruct.DrvStrength = GPIO_DRV_STRENGTH_HIGH;
gpioinitstruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(LED_PORT[Led], &gpioinitstruct);
/* Reset PIN to switch off the LED */
HAL_GPIO_WritePin(LED_PORT[Led],LED_PIN[Led], GPIO_PIN_RESET);
}
用此处提供的函数可以方便验证
上面讲的是用HAL库的方式操作,下面和介绍下最近用寄存器操作的方式,如:pwm,uart
高级定时器TIM1由一个16位的自动装载计数器组成,它由一个可编程的预分频器驱动。他适合多种用途,包含测量输入信号的脉冲宽度(输入捕获),或者产生输出波形(输出比较、pwm、嵌入死区时间的互补pwm等)。其主要特性包括:
1.16位向上、向下、向上/向下自动装载计数器
2.16 位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为 1~65535 之间的任意数值
3. 多达 4 个独立通道:
输入捕获
输出比较
PWM 生成(边缘或中间对齐模式)
单脉冲模式输出
4.死区时间可编程的互补输出
5.使用外部信号控制定时器和定时器互联的同步电路
6.允许在指定数目的计数器周期之后更新定时器寄存器的重复计数器
7.刹车输入信号可以将定时器输出信号置于复位状态或者一个已知状态
8.如下事件发生时产生中断:
更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
输入捕获
输出比较
刹车信号输入
9.支持针对定位的增量(正交)编码器和霍尔传感器电路
10.触发输入作为外部时钟或者按周期的电流管理
PWM脉冲宽度调制模式可以产生一个由 TIM1_ARR 寄存器确定频率、由 TIM1_CCRx 寄存器确定占空比的信号。在 TIM1_CCMRx 寄存器中的 OCxM 位写入’110’(PWM 模式 1)或’111’(PWM 模式 2),能够独立地设置每个 OCx 输出通道产生一路 PWM。必须通过设置 TIM1_CCMRx 寄存器的 OCxPE 位使能相应的预装载寄存器,最后还要设置 TIM1_CR1 寄存器的 ARPE 位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置 TIM1_EGR 寄存器中的 UG 位来初始化所有的寄存器。OCx 的极性可以通过软件在 TIM1_CCER 寄存器中的 CCxP 位设置,它可以设置为高电平有效或低电平有效。OCx 的输出使能通过(TIM1_CCER 和 TIM1_BDTR 寄存器中)CCxE、CCxNE、MOE、OSSI 和 OSSR 位的组合控制。详见 TIM1_CCER 寄存器的描述。在 PWM 模式(模式 1 或模式 2)下,TIM1_CNT 和 TIM1_CCRx 始终在进行比较,(依据计数器的计数方向)以确定是否符合 TIM1_CCRx≤TIM1_CNT 或者 TIM1_CNT≤TIM1_CCRx。根据 TIM1_CR1寄存器中 CMS位的状态,定时器能够产生边沿对齐的 PWM 信号或中央对齐的PWM信号。使用高级定时器TIM1输出4路pwm的方式,复用的IO引脚是PC6(TIM1_CH1),PD2(TIM1_CH2),PC3(TIM1_CH3),PC4(TIM1_CH4)。
RCC->HCLKEN |= 1 << 2;
RCC->HCLKEN |= 1 << 3;
RCC->PCLKEN |= 1 << 10;//RCC_TIM1
GPIOC->AFR |= 0X01000000;//TIM1_CH1
GPIOC->DIRCR |= 1 << 6;//pc6
GPIOC->OTYPER &= ~(1 << 6);
///GPIOC->PUPDR |= 2 << 12;
GPIOC->SLEWCR &= ~(1 << 6);
GPIOD->AFR |= 0X00000100;//TIM1_CH2
GPIOD->DIRCR |= 1 << 2;//PD2
GPIOD->OTYPER &= ~(1 << 2);
///GPIOD->PUPDR |= 2 << 4;
GPIOD->SLEWCR &= ~(1 << 2);
GPIOC->AFR |= 0X00001000;//TIM3_CH1
GPIOC->DIRCR |= 1 << 3;//PC3
GPIOC->OTYPER &= ~(1 << 3);
///GPIOC->PUPDR |= 2 << 6;
GPIOC->SLEWCR &= ~(1 << 3);
GPIOC->AFR |= 0X00010000;//TIM4_CH1
GPIOC->DIRCR |= 1 << 4;//PC4
GPIOC->OTYPER &= ~(1 << 4);
///GPIOC->PUPDR |= 2 << 8;
GPIOC->SLEWCR &= ~(1 << 4);
TIM1->ARR = arr;
TIM1->PSC = psc;
TIM1->CCMR1 |= 6 << 4;//TIM1_CH1 OC_MODE1
TIM1->CCMR1 |= 1 << 3;//OC_MODE1 enable
TIM1->CCMR1 |= 6 << 12;//TIM1_CH2 OC_MODE1
TIM1->CCMR1 |= 1 << 11;//OC_MODE2 enable
TIM1->CCMR2 |= 6 << 4;//TIM1_CH3 OC_MODE1
TIM1->CCMR2 |= 1 << 3;//OC_MODE3 enable
TIM1->CCMR2 |= 6 << 12;//TIM1_CH4 OC_MODE1
TIM1->CCMR2 |= 1 << 11;//OC_MODE4 enable
/*
TIM1->CR1 |= 1<<7;//TIM1 ARP1 enable
TIM1->CR1 |= 1<<4;//TIM1 计数器向下计数
*/
TIM1->CCER |= 1 << 0;//OC1 enable
TIM1->CCER |= 1 << 4;//OC2 enable
TIM1->CCER |= 1 << 8;//OC3 enable
TIM1->CCER |= 1 << 12;//OC4 enable
TIM1->CCR1 = TIM1_CH1_PULSEWIDTH;
TIM1->CCR2 = TIM1_CH2_PULSEWIDTH;
TIM1->CCR3 = TIM1_CH3_PULSEWIDTH;
TIM1->CCR4 = TIM1_CH4_PULSEWIDTH;
TIM1->BDTR |= 1 << 15;
///TIM1->CR1 = 0x0080;//ARPE enable
TIM1->CR1 |= 1 << 0;//TIM1 enable
这里代码使用的是寄存器的方式输出pwm,首先要打开的是RCC的时钟,也就是RCC->HCLKEN的寄存器,根据参考手册,bit0 表示GPIOA,bit1 表示 GPIOB 的时钟,bit2表示GPIOC的时钟,bit3表示GPIOD的时钟 1表示使能,0表示关闭。TIM1主要是用到了CCR,CCMR和CCER寄存器.CCMR寄存器使能对应的预装载和快速使能;CCER寄存器输出使能。CCR寄存器是pwm输出的通道值。
再如用UART0中断收发,使用的是CX32L003 的PA1(UART0_RX) 和PA2(UART0_TX) :
static void uart0Config(void)//串口初始化
{
//USART0
#if 0
///RCC_UART0_CLK_ENABLE;
RCC_UART0_GPIO_ENABLE;
/*
//PA1 - UART0 RX,PA2 - UART0 TX
GPIOA->AFR &= 0XFFFFF00F;//
GPIOA->AFR |= 0X00000550;
*/
GPIO_InitTypeDef GPIO_InitStruct = {0};
/**if UARTx is UART0
GPIO Configuration:
PA1 ------> UART0_RXD
PA2 ------> UART0_TXD
*/
GPIO_InitStruct.Pin = UART_MODULE_RX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF;
GPIO_InitStruct.OpenDrain = GPIO_PUSHPULL;
GPIO_InitStruct.Debounce.Enable = GPIO_DEBOUNCE_DISABLE;
GPIO_InitStruct.SlewRate = GPIO_SLEW_RATE_HIGH;
GPIO_InitStruct.DrvStrength = GPIO_DRV_STRENGTH_HIGH;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = UART_MODULE_RX_AF;
HAL_GPIO_Init(UART_MODULE_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = UART_MODULE_TX_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF;
GPIO_InitStruct.Alternate = UART_MODULE_TX_AF;
HAL_GPIO_Init(UART_MODULE_PORT, &GPIO_InitStruct);
/*
UART_HandleTypeDef Uart0InitStructure;
Uart0InitStructure.Instance = UART_MODULE; // UART0
Uart0InitStructure.Init.BaudRate = baudrate; // 波特率
Uart0InitStructure.Init.BaudDouble = UART_BAUDDOUBLE_DISABLE;// 双波特率禁用
Uart0InitStructure.Init.WordLength = UART_WORDLENGTH_8B; //数据长度
Uart0InitStructure.Init.Parity = UART_PARITY_NONE; // 无校验
Uart0InitStructure.Init.Mode = UART_MODE_TX_RX; // 接收和发送使能
HAL_UART_Init(&Uart0InitStructure);
__HAL_UART_ENABLE_IT(&Uart0InitStructure, UART_IT_RXNE);
#if UART_TX_MODE == UART_TX_MODE_TI
__HAL_UART_ENABLE_IT(&Uart0InitStructure, UART_IT_TC);
#endif
HAL_NVIC_EnableIRQ(UART_MODULE_IRQN); // 中断使能
*/
#endif
#if 1
RCC->HCLKEN |= (1 << 0);//使能RCC_GPIOA
GPIOA->OTYPER &= ~(1 << 2);//PA2推挽输出 UART0_TX_PIN
GPIOA->SLEWCR &= ~(1 << 2);//PA2高电压转换速率
GPIOA->DRVCR &= ~(1 << 2);//PA2高驱动强度
GPIOA->PUPDR |= (1 << 4);//带上拉
GPIOA->AFR |= (5 << 8);//复用为UART0_TX
GPIOA->OTYPER &= ~(1 << 1);//PA1推挽输出 UART0_RX_PIN
GPIOA->SLEWCR &= ~(1 << 1);//PA1高电压转换速率
GPIOA->DRVCR &= ~(1 << 1);//PA1高驱动强度
GPIOA->PUPDR |= (1 << 2);//带上拉
GPIOA->AFR |= (5 << 4);//复用为UART0_RX
RCC->PCLKEN |= (1 << 0);//enable uart0 clk
UART0->SCON &= ~(1 << 9);//单倍波特率
UART0->SCON &= ~(1 << 8);//接收错误帧中断禁止
UART0->SCON |= (1 << 6);//工作模式1
UART0->SCON &= ~(1 << 5);//多机通讯禁止
UART0->SCON |= (1 << 4);//发送/接收使能
UART0->BAUDCR |= (1 << 16);//波特率自动生成
UART0->BAUDCR |= 0x4D;//约为9600 baudrate = (DBAUD + 1)*fpclk /(32 * (BRG[15:0]+1))
NVIC->ISER[0] |= (1 << 6);//UART0_IRQn = 6;
NVIC->IP[1] |= (2 << 22);//中断 6 的优先级参数再IRP1的寄存器里面[23:22]
UART0->SCON |= (3 << 0);//发送和接收完成 中断使能
#endif
}
//在UART0的中断服务函数里面调用该函数
static void uart0_interrupt(void)
{
static unsigned char ucUsart0RecData;
unsigned char usart0_temp_sr = UART0->INTSR;
//关闭接收中断
///USART1->CTRL1 &= ~(0X01 << 5);
/*
if(usart0_temp_sr & UART_FLAG_FE)
{
usart0_temp_sr = UART0->SBUF;
}
else if(usart0_temp_sr & UART_FLAG_RXNE)
{
ucUsart0RecData = UART0->SBUF;
app_uart0_recv_byte(ucUsart0RecData);
}
else
{
//clear interrupt
usart0_temp_sr = UART0->SBUF;
}
*/
if(UART0->INTSR & UART_FLAG_RXNE)
{
UART0->INTCLR |= UART_FLAG_RXNE;
ucUsart0RecData = UART0->SBUF;
uart_receive_input(ucUsart0RecData);///app_uart1_recv_byte(ucUsart0RecData);
}
#if UART_TX_MODE == UART_TX_MODE_TI//该宏定义成立
if(UART0->INTSR & UART_FLAG_TC)
{
UART0->INTCLR |= UART_FLAG_TC;
uart0TcFlag = 1;
}
#endif
}
使用CX32L003的项目是灯带调光的智能家居项目,配合涂鸦模组使用手机app控制开关并调节色温和亮度,如图所示:
使用了6路pwm分别调节灯带几个通道的亮度和色温,色温分冷色和暖色,pwm越大则暖色光向冷色渐变,反之则向暖色光渐变;而亮度则是由pwm输出控制亮暗的程度。pwm越大则亮度越亮,而CX32L003 64k flash 4k SRAM,比较适合做这种小型应用。
|