发新帖本帖赏金 40.00元(功能说明)我要提问
返回列表
[国产单片机]

【国货之光】接力国产替代,恒烁CX32L003替代STM8S003

[复制链接]
10028|3
手机看帖
扫描二维码
随时随地手机跟帖
hejun96|  楼主 | 2021-9-1 11:54 | 显示全部楼层 |阅读模式
本帖最后由 hejun96 于 2021-9-1 16:10 编辑

#申请原创#     @21小跑堂           
     最近ST家的涨价潮和缺货潮下,国产选型越来越火爆,最近使用的一款是CX32L003 封装是TSSOP-20,可替代STM8S003F3 20 pin对应的型号,其资源分布和STM8S003的对比如附图,但STM8S003和CX32L003的引脚资源分配不一样,而CX32L003还是ARM Cortex M0的核,所以只能使用CX32L003推出的HAL库或者是CX32L003的寄存器操作了,下面会介绍两种操作方式。

CX32L003 TSSOP-20封装图

CX32L003 TSSOP-20封装图


       对于任意的一款单片机,首当其冲的就是时钟,时钟作为单片机的心脏为其提供"心跳",决定了定时器的时钟周期和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控制开关并调节色温和亮度,如图所示:
forum.jpg



使用了6路pwm分别调节灯带几个通道的亮度和色温,色温分冷色和暖色,pwm越大则暖色光向冷色渐变,反之则向暖色光渐变;而亮度则是由pwm输出控制亮暗的程度。pwm越大则亮度越亮,而CX32L003 64k flash 4k SRAM,比较适合做这种小型应用。



CX32L003和STM8S003资源对比

CX32L003和STM8S003资源对比

CX32L003时钟树

CX32L003时钟树

CX32L003_User_Manual_V1.0.2_20191129.pdf

5.19 MB

CX32L003参考手册

CX32L003数据手册.pdf

1.25 MB

CX32L003数据手册

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 40.00 元 2021-09-01
理由:恭喜通过原创文章审核!请多多加油哦!

相关帖子

liuchangyin| | 2021-11-18 20:11 | 显示全部楼层
能买到吗?供货是否稳定?价格如何?

使用特权

评论回复
youkebing| | 2021-11-27 22:00 | 显示全部楼层
这个单片机的比较器能输出到管脚吗?

使用特权

评论回复
renesaschina| | 2021-11-30 20:16 | 显示全部楼层
liuchangyin 发表于 2021-11-18 20:11
能买到吗?供货是否稳定?价格如何?

有一批瑞萨的78F0500,有代码生成器,开发方便SSOP30, 8K Flash。

使用特权

评论回复
发新帖 本帖赏金 40.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

10

主题

55

帖子

2

粉丝