CW32

例程资料链接如下:

BD网盘链接:

链接:https://pan.baidu.com/s/1nkBLRwc6rv2qnBTfgyBdgQ
提取码:1kxe

一、简介 

1. L9110风扇模块是一种常见的电机驱动模块,可以用于控制小型直流风扇的转动,常被用于:(1) 电子设备散热:将L9110风扇模块连接到需要散热的电子设备上,通过控制风扇的转速来提高设备的散热效果,保持设备的正常工作温度。(2) DIY电子项目:L9110风扇模块是制作各种DIY电子项目的理想选择。通过将其与微控制器板结合使用,可以构建自己的智能风扇、温度控制系统等。这为爱好者提供了灵活性和创造力的发挥空间。(3) 模型制作:L9110风扇模块也可以在模型制作领域中找到应用。通过将风扇模块嵌入模型中,并通过控制模块来改变风扇的速度和转向,可以增加模型的真实感和互动性。

2.本实验用到了CW32F030C8T6小蓝板、L9110风扇模块、LED交通信号灯模块、轻触微动立式按键开关及Keil5开发环境。

1.png

2.png

风扇三档转速调节系统

二、风扇三档转速调节系统说明

(1)L9110风扇模块 

3.png

L9110风扇模块,可控制正反转,具有安装孔,可以吹灭20cm外的打火机或蜡烛火焰,经常被用于灭火机器人之上。

【连线】:VCC连5V,GND连GND,INA连PA0,INB连PA1

(2)LED交通信号灯模块 

4.png

【连线】:GND连GND,R连PC13,Y连PC14,G连PC15

(3)轻触微动立式按键开关 

5.png

【连线】:VCC连+3.3V,GND连GND,OUT接PB9

三、核心代码

L9110.c:
#include "L9110.h"
#include "GTIM.h"

void L9110_GPIO_Init()      //INA接PA0,INB接PA1
{  
    __RCC_GPIOA_CLK_ENABLE();   
    GPIO_InitTypeDef GPIO_InitStruct;  
    GPIO_InitStruct.IT = GPIO_IT_NONE;   
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出模式  
    GPIO_InitStruct.Pins = GPIO_PIN_0|GPIO_PIN_1;  
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;  
    GPIO_Init(CW_GPIOA, &GPIO_InitStruct);   
    
    PA00_AFx_GTIM2CH1();  //PA0引脚复用为GTIM通道1  
    PA01_AFx_GTIM2CH2();  //PA1引脚复用为GTIM通道2
}

void Turn_around(int16_t value)  //风扇转动函数
{  
    if(value>0)  
    {    
        GTIM_SetCompare1(CW_GTIM2,value); //设置CRR1的值为value    
        GTIM_SetCompare2(CW_GTIM2,0);  
    }  
    else  
    {    
        GTIM_SetCompare1(CW_GTIM2,0);    
        GTIM_SetCompare2(CW_GTIM2,-value);//设置CRR2的为value  
    }
}
GTIM.c:
#include "GTIM.h"

void GTIM2_Init(void) //输出PWM到INA和INB引脚
{  
    RCC_APBPeriphClk_Enable1(RCC_APB1_PERIPH_GTIM2,ENABLE);  //使能APB外设时钟   
    
    GTIM_InitTypeDef GTIM_Initstruct;  
    GTIM_Initstruct.Mode = GTIM_MODE_TIME;  //工作模式-->定时器模式  
    GTIM_Initstruct.OneShotMode = GTIM_COUNT_CONTINUE;//连续计数模式  
    GTIM_Initstruct.ToggleOutState = DISABLE;  //电平反转失能  
    GTIM_Initstruct.Prescaler = BTIM_PRS_DIV64;  //预分频  
    GTIM_Initstruct.ReloadValue =1000-1;//计数重载周期,16bit自动重载寄存器ARR,ARR的值最大为65535  
    GTIM_TimeBaseInit(CW_GTIM2,&GTIM_Initstruct);  
    //定时时长=预分频/计数器时钟源频率*(计数重载周期+1),即T=64/64000000*1000s=1ms  
    GTIM_OCInit(CW_GTIM2,GTIM_CHANNEL1,GTIM_OC_OUTPUT_PWM_LOW);//向GTIMx_CCMR寄存器中的 CCyM 位写入0xF  
    GTIM_OCInit(CW_GTIM2,GTIM_CHANNEL2,GTIM_OC_OUTPUT_PWM_LOW);  
    //当 GTIM2_CNT <= GTIM2_CCR1(GTIM2_CCR2)时,CH1(CH2)通道输出高电平,否则输出低电平  
    GTIM_Cmd(CW_GTIM2,ENABLE); //GTIM2使能   
    
    GTIM_SetCompare1(CW_GTIM2,0);   
    GTIM_SetCompare2(CW_GTIM2,0);  
    //GTIM2_CCR1(GTIM2_CCR2)中的比较值设为0,CH1(CH2) 通道输出保持为低电平
}
    
main.c
#include "main.h"
#include "LED.h"
#include "L9110.h"
#include "GTIM.h"
#include "Key.h"

int main()
{  
    LED_Init();       //三个LED灯用来指示风扇转动状态  
    L9110_GPIO_Init();//L9110风扇模块引脚初始化配置  
    Key_GPIO_Init();  //轻触微动立式按键开关用来进行三档转速调节  
    GTIM2_Init();     //输出PWM到INA,INB引脚  
    while(1)  
    {    
        Key_Scan();     //扫描按键并执行相应功能    
        LED_Indicator();//指示灯  
    }
}

四、效果演示+说明

(1)系统上电处于0档,风扇不转,红色LED灯点亮

 6.png

(2)第一次按下按键开关,系统设置为正向一档,风扇满占空比旋转,风力达到最大,同时红灯熄灭,黄灯点亮,代表风扇顺时针旋转。此后第二次、第三次按下开关,转速依次下降,第四次按下开关,系统回到0档

7.png

(3)第五次按下按键开关,系统设置为反向一档,风扇满占空比旋转,风力达到最大,同时红灯熄灭,绿灯点亮,代表风扇逆时针旋转。此后第六次、第七次按下开关,转速依次下降,第八次按下开关,系统回到0档

8.png

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 16

例程资料链接如下(群文件也可下载):

BD网盘链接:

链接:https://pan.baidu.com/s/1g_fN3YCBw8RINsl9Pe9c6Q

提取码:knxt

一、简介

 1. C-SR04超声波模块是一种常用的测距模块,其通过发射超声波并接收其反射信号来实现测距功能。因其成本低、精度高、使用简便等特点,被广泛应用于以下场景:

(1)避障机器人:HC-SR04超声波模块可以用于避障机器人的距离测量,通过检测障碍物与机器人的距离,实现避障控制。

(2)智能家居:HC-SR04超声波模块可用于智能家居中的人体检测和距离测量。例如,在门口安装超声波模块,可以检测人的接近并触发开门动作,或者用于室内的距离测量和触发自动照明等。

2. 本实验用到了CW32F030C8T6小蓝板、0.96寸OLED显示屏、HC-SR04超声波模块及Keil5开发环境。

1.png

2.png

超声波测距系统

二、超声波模块测距的使用方法

使用流程

连接电源将VCC引脚连接到+5V,GND引脚连接到GND。

连接触发引脚

将Trig引脚连接到单片机的数字输出引脚。

连接回波引脚

将Echo引脚连接到单片机的数字输入引脚。

发送信号

通过向Trig引脚发送一个至少10微秒的高电平触发信号来启动测距过程

接收信号

模块发送触发信号后,自动发射超声波,并等待接收反射信号。当接收到反射信号时,Echo引脚会输出一个高电平信号,持续时间与超声波的往返时间成正比。

计算距离

通过测量Echo引脚输出高电平信号的持续时间,可以计算得到距离,一般使用以下公式计算:

距离 = 高电平持续时间 × 声波在空气中传播的速度 / 2。

重复测量

根据需要可定时测量距离,以实现连续的距离监测。

需要注意的是,HC-SR04超声波模块的测距精度受到多种因素的影响,如温度、超声波传播介质等。在使用过程中,需要结合具体的应用场景和需求进行参数调整和校准,以获得准确的距离测量结果。

三、核心代码

HC_SR04.c:
#include "HC_SR04.h"

extern unsigned int time;

void HC_GPIO_Init(void)  
{  
    __RCC_GPIOB_CLK_ENABLE();   
    GPIO_InitTypeDef GPIO_InitStruct;   
    GPIO_InitStruct.IT=GPIO_IT_NONE;  
    GPIO_InitStruct.Mode=GPIO_MODE_INPUT_PULLDOWN;//下拉输入  
    GPIO_InitStruct.Pins=GPIO_PIN_8;               //Echo  
    GPIO_Init(CW_GPIOB,&GPIO_InitStruct); 
      
    GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;  
    GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;     //推挽输出  
    GPIO_InitStruct.Pins=GPIO_PIN_9;               //Trig   
    GPIO_Init(CW_GPIOB,&GPIO_InitStruct);   
    
    PB09_SETLOW();   //Trig拉低,为输出脉冲触发信号做准备
}

unsigned int Measure_Distance(void) //测距
{  
    unsigned int distance=0;  
     
    SetTrig();     //10us的脉冲触发信号  
    Delay_us(10);  ResetTrig();  
    while(ReadEcho()==0); //等待Echo输出高电平  
    time=0;               //开始记录回波信号脉宽  
    while(ReadEcho()==1); //等待Echo输出低电平       
    distance=time*1.7;//根据声速和时间计算距离,即distance=time*340/2/100  
    /*      
        关于分辨力(mm):          
        定时器每次对time加1是10us,10us=0.01ms,340m/s=340mm/ms          
        计算距离时,最小分辨力为:0.01(ms) * 340(mm/ms) / 2 = 1.7(mm)          
        小于模块标准精度3mm,故测距结果十分精准  
    */      
    return distance; //返回距离,单位mm
}
main.c:
#include "HC_SR04.h"

extern unsigned int time;

void HC_GPIO_Init(void)  
{  
    __RCC_GPIOB_CLK_ENABLE();  
     
    GPIO_InitTypeDef GPIO_InitStruct;   
    GPIO_InitStruct.IT=GPIO_IT_NONE;  
    GPIO_InitStruct.Mode=GPIO_MODE_INPUT_PULLDOWN;//下拉输入  
    GPIO_InitStruct.Pins=GPIO_PIN_8;               //Echo  
    GPIO_Init(CW_GPIOB,&GPIO_InitStruct);  
     
    GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;  
    GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;     //推挽输出  
    GPIO_InitStruct.Pins=GPIO_PIN_9;               //Trig   
    GPIO_Init(CW_GPIOB,&GPIO_InitStruct);   
    
    PB09_SETLOW();   //Trig拉低,为输出脉冲触发信号做准备
}

unsigned int Measure_Distance(void) //测距
{  
    unsigned int distance=0;   
    SetTrig();     //10us的脉冲触发信号  
    Delay_us(10);  
    ResetTrig();  
    while(ReadEcho()==0); //等待Echo输出高电平  
    time=0;               //开始记录回波信号脉宽  
    while(ReadEcho()==1); //等待Echo输出低电平       
    distance=time*1.7;//根据声速和时间计算距离,即distance=time*340/2/100  
    /*      
        关于分辨力(mm):          
        定时器每次对time加1是10us,10us=0.01ms,340m/s=340mm/ms          
        计算距离时,最小分辨力为:0.01(ms) * 340(mm/ms) / 2 = 1.7(mm)          
        小于模块标准精度3mm,故测距结果十分精准  
    */  
    return distance; //返回距离,单位mm
}

四、实验最终现象

3.png

4.png

来源: CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 21

例程资料链接如下:

BD网盘链接:

https://pan.baidu.com/s/1q7b0_AOx_rUxi_XPcJL7zw 

提取码:vp02

一、简介

 AS608是一款集成了指纹图像采集和指纹比对算法的指纹识别模块。它采用了高质量的光学传感器,能够实时采集人体指纹图像,并通过指纹比对算法进行指纹识别。AS608具有高精度、快速、可靠的特点,广泛应用于各种指纹识别场景,如门禁系统、安防系统、考勤系统等。

本实验用到了CW32-48F大学计划板、0.96寸OLED显示屏模块、AS608指纹模块及Keil5开发环境。

1.png

CW32-48F大学计划板

2.png

AS608指纹模块

二、AS608指纹模块使用说明

(1) 引脚说明(该模块共8个引脚,只使用前6个引脚)

3.png

(2) 通讯方法

通过给AS608串口发送特定的指令调用模块内置的算法进行相应的操作。

常用的指令有:

 4.png

5.png

指令格式有三种:命令包格式、数据包格式和结束包格式。

6.png

 以命令包格式为例,包头是2个字节的数据,固定为0xEF01;芯片地址4个字节,默认是0xFFFFFFFF;包识别用来区分指令类型,如命令包固定为0x01,数据包格式固定为0x02;包长度指这一条指令有多少个重要的数据,包长度=包长度至校验和(指令、参数或数据)的总字节数,包含校验和,但不包含包长度本身的字节数;指令就是需要AS608执行的操作,参数和具体的指令有关,不同的指令,参数的长度和数值都有所不同;校验和是为了确保串口通讯正确。

模块接收到命令后会产生应答,将有关命令执行情况与结果上报给上位机,上位机只有在收到模块的应答包后才能确认模块收包情况与指令执行情况。模块应答包中包含一个参数:确认码。确认码表示执行指令完毕的情况。

 7.png

确认码定义:

00H:表示指令执行完毕或 OK;

01H:表示数据包接收错误;

02H:表示传感器上没有手指;

03H:表示录入指纹图像失败;

...

(3) 指纹录入实现流程

录入指纹图像

当手指放在光学指纹窗口时,模块自动拍下指纹图像。

生成特征

记录图像中的指纹特征,并将该特征存入缓存区。

再次录入图像

当手指放在光学指纹窗口时,再次拍下指纹图像。

再次生成特征

记录二次图像中的指纹特征,并将该特征存入另一缓存区。

精确比对两枚指纹特征

对录入的两个指纹特征进行比对。

合并特征(生成模板)

比对成功无误后,将两个特征合并成一个指纹模板。

储存模板

将生成的模板存储到到AS608内部Flash,存储时需指定存储地址,即指纹ID。

上面以指纹录入功能为例,简要介绍了功能实现的基本流程,更多详情请参考AS60x指纹识别SOC用户手册和ATK-AS608指纹识别模块用户手册。

三、核心代码

main.c:
#include  "main.h"
#include  "Uart.h"
#include  "LED.h"
#include  "AS608.h"
#include  "OLED.h"
#include  "Delay.h"
#include  "BTIM.h"
#include  "Key.h"

uint8_t flag_add=0;          //指纹录制标识位
uint8_t flag_passport=0;     //密码输入标识位
uint8_t flag_key_function=0; //按键功能标识位
const uint16_t passpot=123;  //管理员密码

/*-----------------------刷指纹-----------------------*/
void AS608_MatchFingerPrints(void)
{  
    if(Is_Press==1)       //有手指按下  
    {    
        SearchResult seach;    
        uint8_t result;    
        result=PS_GetImage();    
        if(result==0x00)//获取图像成功     
        {        
            result=PS_GenChar(CharBuffer1);      
            OLED_Clear();      
            if(result==0x00) //生成特征成功      
            {          
                result=PS_HighSpeedSearch(CharBuffer1,0,6,&seach);  //自定义检索ID为0~6的指纹库,最大范围为0~300        
                if(result==0x00)//搜索成功        
                {                
                    OLED_ShowString(2,1,"      ID=      ");  //指纹识别成功后自定义显示内容          
                    OLED_ShowNum(2,10,seach.pageID,1);          
                    OLED_ShowString(3,1,"    Welcome!    ");          
                    
                    /*                    
                            指纹识别成功后的自定义功能                                                  
                                                                */        
                }        
                else         
                {          
                    OLED_ShowString(1,1,"    Refuse!    ");  //指纹识别失败,遭到系统拒绝        
                }              
            }      
            else      
            {        
                OLED_ShowString(1,1,"    Error!     ");  //指纹识别过程出现错误,3秒后重启识别功能可再次识别        
                OLED_ShowString(2,1,"      3       ");        
                Delay_s(1);        
                OLED_ShowString(2,1,"      2       ");        
                Delay_s(1);        
                OLED_ShowString(2,1,"      1       ");      
            }      
            Delay_s(1);    
        }    
        Display_Meum();     
        flag_passport=0;  
    }  
}

/*-----------------------录指纹-----------------------*/
void AS608_AddFingerPrints(void)
{  
    uint8_t result,steps=0;  
    flag_add=1;  
    OLED_Clear();  
    OLED_ShowString(1,1,"AddFingerPrints");  
    while(1)  //在指纹录制环节中循环  
    {    
        switch (steps)    
        {      
            case 0:          //录制环节0        
            flag_key_function=1;        
            OLED_ShowString(2,1,"Choose ID:");  //指纹ID选择界面        
            OLED_ShowNum(2,11,Finger_ID,1);        
            OLED_ShowString(3,1,"               ");        
            OLED_ShowString(4,1,"back");        
            OLED_ShowString(4,8,"+");        
            OLED_ShowString(4,13,"ok");        
            switch(Key_Scan())        
            {          
                case 1:         //退出指纹录制,返回主界面,            
                Display_Meum();            
                return ;          
                case 2:      //选择录入的指纹对应的ID            
                if(Finger_ID!=6)  Finger_ID++;            
                else              Finger_ID=1;            
                break;          
                case 3:              //进入下一个环节            
                steps=1;            
                break;        
            }      
            break;       
            
            case 1:          //录制环节1        
            if(Key_Scan()!=0){Display_Meum();return ;};  //按任意键退出        
            OLED_ShowString(2,1,"               ");        
            OLED_ShowString(3,1,"Please press...");     //操作提示        
            OLED_ShowString(4,1,"               ");        
            result=PS_GetImage();        //首次获取指纹图像        
            if(result==0x00)         
            {          
                result=PS_GenChar(CharBuffer1);//生成特征1          
                if(result==0x00)          
                {            
                    OLED_ShowString(3,1," OK! Get it... ");            
                    Delay_s(1);            
                    OLED_ShowString(2,1,"               ");            
                    steps=2;        //进入下一个环节                      
                }                
            }              
            break;       
            
            case 2:          //录制环节2        
            if(Key_Scan()!=0){Display_Meum();return ;};  //按任意键退出        
            OLED_ShowString(3,1,"Press Again...");      //操作提示        
            result=PS_GetImage();         //再次获取指纹图像        
            if(result==0x00)         
            {          
                result=PS_GenChar(CharBuffer2);//生成特征2          
                if(result==0x00)          
                {            
                    OLED_ShowString(3,1," OK! Get it... ");            
                    Delay_s(1);            
                    steps=3;        //进入下一个环节            
                }            
            }            
            break;
      
         case 3:          //录制环节3        
         result=PS_Match();    //特征1与特征2进行匹配        
         if(result==0x00)      //匹配成功        
         {          
             steps=4;          //进入下一个环节          
             OLED_ShowString(3,1,"  Matching...  ");        
         }        
         else                  //匹配失败        
         {          
             OLED_ShowString(2,1,"     Fail!     ");          
             OLED_ShowString(3,1,"       3       ");          
             Delay_s(1);          
             OLED_ShowString(3,1,"       2       ");          
             Delay_s(1);          
             OLED_ShowString(3,1,"       1       ");          
             steps=0;          //回到录制环节0          
         }        
         Delay_s(1);        
         break;
      
         case 4:          //录制环节4        
         result=PS_RegModel();  //合并特征,生成指纹模板        
         if(result==0x00)           
         {          
             steps=5;          //进入下一个环节          
             OLED_ShowString(3,1,"      OK!      ");        
         }        
         else                       
         {          
             OLED_ShowString(2,1,"     Fail!     ");          
             OLED_ShowString(3,1,"       3       ");          
             Delay_s(1);          
             OLED_ShowString(3,1,"       2       ");          
             Delay_s(1);          
             OLED_ShowString(3,1,"       1       ");          
             steps=0;        
         }        
         Delay_s(1);        
         break;       
         
         case 5:          
         result=PS_StoreChar(CharBuffer2,Finger_ID);   //储存指纹        
         if(result==0x00)         
         {                    
             flag_key_function=0;          
             OLED_ShowString(3,1,"AddSuccessfully");          
             Delay_s(1);          
             Display_Meum();        //回到主页面          
             return ;        
         }                  
         break;            
     }      
    }
}

/*-----------------------主页面-----------------------*/
void Display_Meum()   
{  
    OLED_Clear();  
    flag_add = 0;  
    flag_key_function = 0;  
    if(PS_ValidTempleteNum(&validFinger_num)==0xff)  //读取指纹模块当下信息参数    
        OLED_ShowString(2,1,"  ERROR!!!  ");     //提示读取失败,原因可能是线没接好或者模块损坏  
    else   
    {    
        OLED_ShowString(1,1,"----Welcome----");      
        OLED_ShowString(2,1,"ValidNum:");    
        OLED_ShowNum(2,10,validFinger_num,1);    
        OLED_ShowString(4,1,"add   delet   ");    
        if(delet_num==0) OLED_ShowString(4,15,"+");    
        else OLED_ShowNum(4,15,delet_num,1);  
    }
}

/*-----------------------密码输入-----------------------*/
void PassportInput()    
{  
    static uint8_t i=0;  
    flag_key_function=1;  
    OLED_ShowString(1,1,"No access!");      //密码输入提示   
    OLED_ShowString(2,1,"Input Passport:");  
    OLED_ShowNum(3,6,passport_num1,1);  
    OLED_ShowNum(3,8,passport_num2,1);  
    OLED_ShowNum(3,10,passport_num3,1);  
    OLED_ShowString(4,1,"+");  
    if(i==0)  
    OLED_ShowString(4,6,"^");  
    OLED_ShowString(4,12,"back");  
    switch(Key_Scan())  
    {    
        case 1://加      
        if(i==0) {passport_num1++;if(passport_num1==10) passport_num1=0;}      
        if(i==1) {passport_num2++;if(passport_num2==10) passport_num2=0;}      
        if(i==2) {passport_num3++;if(passport_num3==10) passport_num3=0;}      
        if((passport_num1*100+passport_num2*10+passport_num3)==passpot)      
        {        
            OLED_ShowNum(3,10,passport_num3,1);        
            Delay_ms(500);        
            OLED_Clear();        
            OLED_ShowString(2,1,"    Access!    ");        
            access=1;       //打开指纹录制/删除权限        
            PA07_SETLOW();  //LED全部点亮,指示权限全开        
            PA08_SETLOW();        
            PC13_SETLOW();        
            Delay_s(2);        
            Display_Meum();        
            flag_passport=0;          
            passport_num1=0;        
            passport_num2=0;        
            passport_num3=0;        
            i=0;      
        }      
        break;    
        case 2://  切换数字      
        if(++i==3) i=0;      
        if(i==1)      OLED_ShowString(4,6,"  ^  ");       
        else if(i==2) OLED_ShowString(4,6,"    ^");       
        else if(i==0) OLED_ShowString(4,6,"^    ");       
        break;    
        case 3://退出      
        passport_num1=0;      
        passport_num2=0;      
        passport_num3=0;      
        Display_Meum();      
        flag_passport=0;      
        i=0;      
        break ;    
    }
}

/*------------------指纹录制/删除权限管理-------------------*/
void Permission_Management(void)
{  
    if(Key_Scan()==20)   //若返回键值为20,则打开密码输入标识  
    {
        OLED_Clear();    
        flag_passport=1;   
    }  
    if(flag_passport==1)//若密码标识打开,则进入密码输入界面    
    PassportInput();
}

/*--------------------------主函数--------------------------*/
int main()
{  
    LED_Init();             //3个LED指示灯  
    OLED_Init();           //OLED屏幕显示  
    BTIM_Init();           //启动定时器配合应答包接收  
    UART2_Init();        //与AS608进行串口通信,波特率为57600  
    Key_GPIO_Init();       //三个按键  
    AS608_WAK_GPIO_Init(); //WAK引脚,感应手指按下  
    Display_Meum();        //屏幕主界面  
    while(1)  
    {      
        AS608_MatchFingerPrints();  //指纹识别    
        Permission_Management();    //权限管理  
    }
}

四、最终实验现象

8.png

录入指纹
9.png

识别指纹
10.png

删除指纹

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 28

本章针对CW32F030C8T6的时钟树进行详细解析,续第一章启动文件的相关讲解。

上一章讲到,单片机的启动文件会使用单片机内部的RC振荡器作为单片机的启动时钟,并且该时钟频率被设置为48MHz。但讲解有疏漏,根据编程手册的描述,该48MHz仅为HSI频率,而非输送到时钟总线上的频率,在进入时钟总线之前,该时钟会被分频(也就是降低频率),在不进行任何配置的情况下,这个48M的信号会被6分频。

1.png

这个图包含很多信息,但是图中的彩色字信息并非全部来自此图,更多的信息需要结合代码和寄存器描述来获得:

1.HSI被配置为48M是通过启动文件中的SystemInit函数得知的。

2.系统选择HSI作为启动时钟是通过编程手册“时钟与复位”一章得知的。

3.HSI之后的预分频器被设置为6分频,是通过查看指示该分频器的寄存器得知的。

下面以信息3为例,讲解如何查看此图中的相关寄存器:

  • 在经过SYSCTRL_CR0.SYSCLK寄存器选择之后,系统时钟SysClk会在经过分频后送入内核与各个外设,这一信息流程图与粉色箭头已经清晰展示。

  • 在手写箭头指向的方框中,有1、...、16的字样,表示该预分频器可以进行这些倍数的分频。所谓分频比如48M,6分频,其分频结果就是48M/6 = 8M,2分频就是24M,简单的除法。

  • 方框下方是该分频器对应的控制寄存器,寄存器的名字并不一定完全叫这个,但是这个名字可以很容易就被理解为预分频器控制相关的寄存器,该寄存器名字拆解版本是:SYS(系统)CTRL(控制)HSI.DIV(分频),这套命名系统是通用的,即使使用的是别的单片机,也可以根据这套命名规则快速确认寄存器的功能。

  • 通过查看编程手册时钟与复位章节的寄存器描述,此寄存器DIV位的值默认为6。而HSI频率的设置则是该寄存[10:0]位的TRIM位决定的。

2.png

只需要沿着紫色箭头的方向配置相关的寄存器,单片机就能够正常启动,但这一步并不需要开发者亲自去做,芯片厂家提供的启动文件和库可以自动完成这一步。但不论怎么说RC振荡器的精度有限,且8M的速度放在48MHz主频的内核上也确实不够看,因此大部分时候,都需要使用外部晶振提供的时钟,通过锁相环倍频之后达到48M,最后通过时钟线送入内核和外设。

下面讲解怎么配置才能得到48MHz的高精度高速时钟信号:

3.png

首先,电路板上需要有一个在范围内的晶体,晶体的两个引脚需要在外围电路的配合下连接到单片机的晶体输入引脚,同时IO需要工作在正确的工作模式。

4.png 

硬件部分准备完成了,接下来就是配置HSE和PLL相关的寄存器了。下面是相关的配置代码:

5.png

  • 首先需要打开HSE功能,允许单片机接收HSE提供的震荡信号。

  • 然后配置PLL的分频系数,这将决定锁相环的输出频率,此处设置为1分频,也就是不分频。

  • 使能PLL功能,并告知用到的时钟源、时钟频率、倍频系数,这对应三个入口参数。

  • 将flash的等待周期设置为3个时钟周期,部分单片机需要进行这一步操作,原因会在后面细说。

  • 进行时钟切换,按照注释完成准备工作之后即可切换。

先查看HSE使能函数: 

6.png

仅展示主体部分,函数注释未列出,但CW32的时钟配置库函数注释相对来说很详细,推荐配置都写在函数注释里面,不懂得寄存器配置的小伙伴可以直接根据推荐进行配置,如果有更深入的需求,直接查看芯片手册对应的寄存器描述即可。

后续的参数配置直接根据注释进行推荐配置即可,在PLL与HSE相关的寄存器配置完成之后,48M的时钟信号就已经产生了,只不过系统的时钟源还不是这个(记得那个梯形的选择器吗?),下一步就是切换时钟源,让系统工作在48M的频率下。

切换时钟?我知道你很急,但是先别急,虽然一般情况下确实可以直接切换了,但是CW32有个需要注意的地方,那就是flash的配置。

为什么CW32需要进行flash配置之后才能切换时钟源?

我们都知道,写的程序都存储在flash中,等到需要执行程序的时候,CPU会和flash进行通信,取出flash中的指令然后执行。问题来了,既然程序需要通过某种通信方式传输到CPU,那这个通信传输的速度一定有个上限,这个上限就是flash的读写速度上限,当flash的实际读写速度与其允许的读写速度不匹配时,flash与CPU之间的通信就会出现问题,之后就可以理解为CPU有高速取指令需求,但是flash无法在这个速度下跟上CPU的请求速度,这个通信就断了,程序就不动了。因此我们需要设置flash,让他多等几个时钟周期再响应,这样flash就能适应更高频率的读取请求,并且在此程序的配置中,需要使能预取指令和缓存功能,这两个功能可以让flash与CPU更好地配合以实现单片机的高速运行。障碍都解决了,接下来真的只需要切换时钟即可。

对比部分:这一部分会列出stm32f103c8t6单片机的标准库上电时钟设置代码进行对比。

7.png

8.png

这里的条件编译,我选择最后一条,如果需要上电设置为别的频率,只需要在同一个文件中把宏定义注释取消即可。

9.png

下面列出设置时钟到72M函数的一部分,此函数与CW32的时钟初始化顺序几乎是一模一样,首先需要将HSE使能并等待时钟稳定,之后设置PLL的参数,配置flash为2个等待周期,再使能PLL输出,等待PLL时钟稳定再切换时钟。

10.png11.png

12.png

可以看出,即使是不同的芯片,他们在大部分地方的操作也是一样的,只是一些细节上有些许不同。

总结:

1.本章简单展示了如何查找手册来配置寄存器,且再次强调库函数的本质就是操作寄存器。

2.单片机都会有一个时钟树,时钟树的图可以在编程手册(不是数据表和内核手册)中找到。

3.部分单片机想要工作在高工作频率下,需要设置flash等待时间并打开缓存和预取指令使能。

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 25

前言 

电池备份(VBAT)功能的实现方法,一般是使用 MCU 自带的 VBAT 引脚,通过在该引脚连接钮扣电池,当系统电源因故掉电时,保持 MCU 内部备份寄存器内容和 RTC 时间信息不会丢失。 

本文档介绍了如何基于 CW32 系列 MCU,通过增加简单的外部电路配合软件实现 VBAT 功能,在系统电源掉 电后仍能保持 RTC 时钟正常计时,以及如何降低系统功耗,从而延长后备电池的使用寿命。

1 、电路设计 

对于自带 VBAT 引脚的 MCU,MCU 内部有对 VBAT 电源和系统电源的管理单元,保证在系统电源掉电后,及 时切换 VBAT 引脚电源给备份域供电,保证 RTC 正常工作。 

对于没有 VBAT引脚的 CW32,要实现类似的功能,可以在外部进行后备带电池和系统电源的切换,如下图所示:

1.png

后备电池(B1)提供的备用电源 VBAT 和系统电源 VDDIN 通过 2 个肖特基二极管(D1)合路,合路后的 电源 VDD 给 MCU 的数字域 DVCC 和模拟域 AVCC 进行供电。系统电源 VDDIN 通过 R3、R4 电阻分压得到 WAKEIO 信号,连接到 MCU 的 IO 引脚。注意遵循如下规则:

 1. Vwakeio 要大于 MCU IO 口的 Vih;

 2. VDDIN 必须高于 Vb1 在 0.4V 以上,否则如果 VDDIN 和 Vb1 相等,在系统电源正常时,后备电池也会有一定 的泄放电流,不利于节省后备电池电量。

2 、程序设计 

程序启动后正常初始化时钟、IO、RTC 以及 OELD,循环中检测系统电源是否存在,如存在则读取 RTC 时间 并显示。 

当系统电源 VDDIN 因故掉电,则关闭 OLED 电源,并进入 DeepSleep 低功耗睡眠模式。 

当系统电源 VDDIN 恢复供电时,产生高电平中断,唤醒 MCU,退出 DeepSleep 低功耗睡眠模式。

3 、参考代码

int32_t main(void)
{
    RCC_Configuration();     // 时钟配置
    GPIO_Configuration();     //GPIO配置
    OLED_Init();          //OLED显示屏初始化配置
    dis_err("RTC_TestBoard");   // 显示
    FirmwareDelay(5000000);   // 增加延时防止过早休眠影响程序烧写
    RTC_init();          //RTC时钟初始化
    //DeepSleep 唤醒时,保持原系统时钟来源
    RCC_WAKEUPCLK_Config(RCC_SYSCTRL_WAKEUPCLKDIS);
    ShowTime();       // 获取时间数据
    displaydatetime();    // 显示当前时间
    while(1)
    {
        if( 0==PB05_GETVALUE() )  // 循环检测是否掉电
        {
            PA05_SETHIGH();   // 关 OLED 电源
            SCB->SCR = 0X04;  //DeepSleep
            __WFI();       //MCU 进入DeepSleep模式以节省功耗
            OLED_Init();     // 外部电源接入后唤醒,重新初始化 OLED
        }
        else
        {
            ShowTime();      // 获取时间数据
            displaydatetime();   // 显示当前时间
        }
    }
}

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct= {0};
    __RCC_GPIOB_CLK_ENABLE();         // 开 
    GPIOB 时钟GPIO_InitStruct.IT  = GPIO_IT_RISING;    // 使能上升沿中断
    GPIO_InitStruct.Mode  = GPIO_MODE_INPUT;  // 输入模式
    GPIO_InitStruct.Pins = GPIO_PIN_5 ;    //PB05,连接 WAKEIO 网络
    GPIO_Init(CW_GPIOB, &GPIO_InitStruct);  // 初始化 IO
    GPIOB_INTFLAG_CLR(bv5);        // 清除 PB05 中断标志
    NVIC_EnableIRQ(GPIOB_IRQn);      // 使能 PB05 中断
}

4 、实际测试

使用 CW32L031C8T6 设计了用于测试后备电池功能的评估板,实物如下图所示:

2.png

使用 3V 的 CR2032 钮扣电池,实测电池电压为 3.14V;VDDIN 使用可调节数字电源,设置为 3.54V,保证 VDDIN >= Vb1 + 0.4V;D1 实测合路后的电源电压为 3.21V。

3.png

4.1 测试数据 

实际测试时,断开 J4 跳线接入万用表,设置万用表为电流测试档位。 

1. 关闭 VDDIN 电源输入,MCU 检测到无外电输入,关闭 OLED 显示,进入 DeepSleep 模式,实测此时 B1 电流为 +0.95μA。 

2. 打开 VDDIN电源输入,MCU被高电平中断从 DeepSleep状态唤醒到正常状态,OLED正常显示当前时间, 实测此时 B1 电流为 -75nA(负电流是因为 D1 处于反向偏置状态,有小的反向漏电流)。 

测试结果符合电路设计预期,以 CR2032 电池容量为 200mAH 计算,则电池可用时间为 210526 小时,合计 24 年(不考虑电池和产品寿命),可实现超长待机时间,完全满足各种低功耗产品对 RTC 后备电池容量需求。

5 、附件 

5.1 RTC_TestBoard 单板原理图

4.png

5.png

来源:武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 36

1.PID温控系统是一种常用的控制系统,用于实现对温度变量的精确控制。PID算法根据当前的温度误差以及误差的变化率,计算一个控制信号,用于调节加热器的输出。以下是PID算法的三个主要组成部分:

①比例(Proportional)控制:比例控制是根据当前的温度误差来计算控制信号。具体而言,通过将设定温度与实际温度之间的差异称为误差,然后将误差乘以一个比例增益参数,得到一个修正值。这个修正值与控制设备的输出信号相加,以调整温度控制。

②积分(Integral)控制:积分控制用于处理长期的温度误差。它通过对温度误差进行积分来计算一个积分误差。积分误差乘以积分增益参数,并且在一段时间内进行积累,得到一个修正值。积分控制可以帮助消除持续的稳态误差,使系统更快地达到设定温度。

③微分(Derivative)控制:微分控制用于处理温度变化的速率。它通过计算温度误差的变化率,即误差的导数,得到一个微分值。微分值乘以一个微分增益参数,用于调整修正值。微分控制可以帮助系统更快地响应温度变化,以防止过冲。

通过结合比例、积分和微分部分的修正值,PID控制算法可以计算出最终的控制信号。这个控制信号会被传递给加热器,以控制温度的变化。

2.本实验用到了CW32-48大学计划开发板OK、温控实验模块及Keil5开发环境。


1.png

CW32-48大学计划开发板OK   

2.png

温控实验模块   

3.png 

4.png

温控模块电路原理图

3.PID控制算法的具体原理可参考以下链接中的文章

1)https://zhuanlan.zhihu.com/p/39573490

2)https://zhuanlan.zhihu.com/p/3473726243)https://zhuanlan.zhihu.com/p/41962512
利用热敏电阻采集温度的原理及方法可参考往期文章及视频。

4.核心代码

mian.c:

#include "config.h"

unsigned char face = 0;       //界面变量
unsigned char face_brush = 0; //界面刷新频率控制

void InitSystem(void)  
{  
    RCC_Configuration();        //时钟配置  
    ADC_Configuration();        //ADC采集通道配置,采集NTC热敏电阻电压  
    PID_Configuration();        //PID参数配置  
    GPIO_KEYS_Configuration(); //按键GPIO配置  
    PWM_Init();                  //两路PWM输出初始化  
    Lcd_Init();                 //TFT屏幕初始化  
    BTIM_Init();                //定时器初始化}

void Interface(void)  //人机交互界面
{  
    if ( face_brush > 200 )  //200ms刷新一次  
    {    
        face_brush = 0;    
        switch(face)    
        {      
            case 0://显示PV和SV,该界面下,可以设定SV        
            TFTSHOW_STRING_HEADLINE(0,0,"  PID  Control  ");        
            TFTSHOW_STRING(2,0,"REAL_Temper(℃):");        
            TFTSHOW_STRING(4,0,"   P V:       ");        
            TFTSHOW_FLOAT_NUMBER(4,8,pid.Pv);        
            TFTSHOW_STRING(6,0,"SET_Temper(℃):");        
            TFTSHOW_STRING(8,0,"   S V:       ");        
            TFTSHOW_FLOAT_NUMBER(8,8,pid.set_Sv);        
            break;      
            case 1://该界面下,可以设定P参数        
            TFTSHOW_STRING_HEADLINE(0,0,"  PID  Control  ");        
            TFTSHOW_STRING(2,0,"SET PID Control:");        
            TFTSHOW_STRING(4,0,"    P :       ");        
            TFTSHOW_INT_NUMBER(4,8,pid.set_Kp);        
            break;      
            case 2://该界面下,可以设定I参数        
            TFTSHOW_STRING_HEADLINE(0,0,"  PID  Control  ");        
            TFTSHOW_STRING(2,0,"SET PID Control:");        
            TFTSHOW_STRING(4,0,"    I :       ");        
            TFTSHOW_FLOAT_NUMBER(4,8,pid.set_Ki);        
            break;      
            case 3://该界面下,可以设定D参数        
            TFTSHOW_STRING_HEADLINE(0,0,"  PID  Control  ");        
            TFTSHOW_STRING(2,0,"SET PID Control:");        
            TFTSHOW_STRING(4,0,"    D :       ");        
            TFTSHOW_INT_NUMBER(4,8,pid.set_Kd);        
            break;      
            case 4://该界面下,可以设定Out0,即修正值        
            TFTSHOW_STRING_HEADLINE(0,0,"  PID  Control  ");        
            TFTSHOW_STRING(2,0,"SET PID Control:");        
            TFTSHOW_STRING(4,0,"   OUT0 :      ");        
            TFTSHOW_INT_NUMBER(4,10,pid.set_Out0);        
            break;    
        }  
    }
}

int main()           //主函数
{  
    InitSystem();      //系统初始化  
    while(1)  
    {    
        PID_Calc();      //PID运算    
        Interface();     //人机交互界面    
        Keys_Function(); //按键控制      
    }
}

pwm.c:

#include "pwm.h"

void PWM_Init(void)
{  
   RCC_APBPeriphClk_Enable1(RCC_APB1_PERIPH_GTIM2,ENABLE); //使能GTIM2时钟
    
   __RCC_GPIOA_CLK_ENABLE();   //使能GPIOA时钟
  
   PA01_AFx_GTIM2CH2();        //打开PWM输出通道  
   PA02_AFx_GTIM2CH3();
  
   GPIO_InitTypeDef GPIO_InitStruct;
  
   GPIO_InitStruct.IT = GPIO_IT_NONE;   
   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  //推挽输出模式  
   GPIO_InitStruct.Pins = GPIO_PIN_1|GPIO_PIN_2;  
   GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;  
   GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
  
   GTIM_InitTypeDef GTIM_Initstructure;     //通用定时器  
   GTIM_Initstructure.Mode=GTIM_MODE_TIME;  //计数模式  
   GTIM_Initstructure.OneShotMode=GTIM_COUNT_CONTINUE; //连续计数  
   GTIM_Initstructure.Prescaler=GTIM_PRESCALER_DIV64; //预分频  
   GTIM_Initstructure.ReloadValue=2000-1; //ARR,计数重载周期2000  
   GTIM_Initstructure.ToggleOutState=DISABLE;  
   GTIM_TimeBaseInit(CW_GTIM2,&GTIM_Initstructure);
  
   GTIM_OCInit(CW_GTIM2,GTIM_CHANNEL3,GTIM_OC_OUTPUT_PWM_LOW); //GTIM2输出比较,CH3、CH2  
   GTIM_OCInit(CW_GTIM2,GTIM_CHANNEL2,GTIM_OC_OUTPUT_PWM_LOW); //有效占空比为低电平  
   GTIM_Cmd(CW_GTIM2,ENABLE); //使能GTIM2
}

void PWM1_Output(uint32_t value)
{  
   GTIM_SetCompare3(CW_GTIM2,value);  //设置GTIM2通道3的CCR
}

void PWM2_Output(uint32_t value)
{  
   GTIM_SetCompare2(CW_GTIM2,value);  //设置GTIM2通道2的CCR
}

void PWM_ALL_Output(uint32_t value)  //PWM1、2同步输出
{  
      PWM1_Output(value);  
      PWM2_Output(value);
}

pid.c:

#include "pid.h"

PID pid;    //定义PID结构体变量pid

void PID_Configuration(void)   //PID参数初始化配置 
{  
    pid.Sv     = 55;
    
  pid.Kp     = 350;  //比例系数  
  pid.Ki     = 10;   //积分系数  
  pid.Kd     = 38;   //微分系数
  
  pid.Ek_1   = 0;    //上一次偏差  
  pid.T      = 400;  //PID计算周期
  
  pid.cnt    = 0;      
  pid.cycle  = 2000; //PWM周期  
  pid.Out0   = 500;  //PID修正值
  
  pid.set_Sv = pid.Sv;  
  pid.set_Kp = pid.Kp;  
  pid.set_Ki = pid.Ki;  
  pid.set_Kd = pid.Kd;  
  pid.set_Out0 = pid.Out0;
}

float Get_Pv(void)  //Pv意为当前测量值,即当前温度
{  
    return Get_Temperture();
}

void PID_Calc(void)    //PID算法
{  
    float Pout,Iout,Dout;  
    float out;
  
  if ( pid.cnt > pid.T )   //控制计算周期  
  {    
      pid.cnt = 0;
    
    pid.Pv = Get_Pv();           
    pid.Ek = pid.Sv - pid.Pv; //计算偏差    
    pid.SumEk += pid.Ek;  //偏差累积

    
    Pout = pid.Kp * pid.Ek;  //比例控制
    
    Dout = pid.Kd * (pid.Ek - pid.Ek_1);  //微分控制
    
    if(pid.Pv>(pid.Sv-1))   //当测量值非常接近目标值的时候加入积分控制    
    {      
        Iout = pid.Ki * pid.SumEk;           //积分控制      
        out = Pout + Iout + Dout + pid.Out0;     
    }    
    else   out  = Pout + Dout + pid.Out0;  //测量值距离目标值较远时只使用PD控制
    
    if ( out > pid.cycle ) pid.Out = pid.cycle; //限幅    
    else if ( out < 0 )    pid.Out = 0;    
    else                   pid.Out = out;    
    PWM_ALL_Output(pid.Out);  //控制PWM输出    
    pid.Ek_1 = pid.Ek;  //进行下一次PID运算之前,将本次偏差变为上次偏差  
   }
}

5.实验最终现象

5.png

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 27

描述

简介:基于武汉芯源半导体CW32F030C8T6和启英泰伦CI-C22GS02制作的智能语音分类垃圾桶,能够实现识别语音来判断垃圾种类,同时打开相应的垃圾桶,可外接普通按键或者触摸按键,也可以外接红外接近传感器来打开对应的垃圾桶。

项目说明

1.jpg

2.jpg

3.jpg

本次设计采用武汉芯源半导体的CW32F030C8T6作为主控,主要负责和离线语音模块的串口通讯,负责输出4路PWM信号、驱动IPS显示屏、控制2路MOS驱动电路(控制风扇和灯光)、驱动4个WS2812B灯。语音识模块采用启英泰伦CI-C22GS02模块(主芯片采用Cl1122,支持本地200条命令词以下的离线语音识别),主要负责语音识别和播报语音,可通过串口与MCU通讯。

项目相关功能介绍介绍

1.离线语音模块的命令词可自行定制,最多可录入200个命令词,目前已录入了168条垃圾名词和一些控制指令。当语音模块识别到正确的命令词后,串口输出规定协议的数据。语音模块也可以接收规定协议的串口数据来播报指定的语音。离线语音模块的串口1和MCU主控的串口2使用跳线连接,方便分别开发调试。

4.jpg

5.jpg

2.主控输出4路PWM信号(周期20us,频率50Hz),可直接驱动4路舵机。

6.jpg

3.四个WS2812B灯作为4类垃圾的指示灯,语音模块在识别到正确的语音后,由CW32主控来控制指定的灯点亮对应的颜色。

7.jpg

4.电路采用直流12V供电,经DC-DC降压后输出5.2V,5.2V再经LDO降压后输出3.3V。其中CI-C22GS02模块、舵机、WS2812B、外部灯光和风扇供电均采用5.2V供电,MCU主控、IPS显示屏采用3.3V供电。

8.jpg

5.电路设计了2路MOS驱动电路,可外接5V的小风扇和5V供电的灯。另外预留了2种屏幕接口,支持8针的SPI接口的屏幕或者4针的I2C接口的屏幕。预留3组扩展接口,将剩余的IO口全部引出,方便扩展。

9.jpg

10.jpg

硬件部分介绍

智能语音分类垃圾桶主要包含主控板、显示屏、独立按键模块、加装舵机的垃圾桶模型、12V电源适配器。

软件部分介绍

软件分两部分,一部分是针对CW32F030C8T6的程序开发,另一部分是针对CI-C22GS02离线语音模块的命令词、语言模型、声学模型以及固件的制作。

1. CW32F030C8T6的程序开发。采用Keil5开发,借鉴了CW32官方例程和开源平台上一些大佬的程序。程序中涉及GPIO、DMA、定时器、ADC、UART、PWM、SPI等功能的实现。

因本人水平有限,写的程序只能说能用,但不保证好用。里面使用了大量的全局变量,还有很多改进的地方。

下面展示了部分程序。

(1)主程序

11.jpg

12.jpg

(2)串口发送程序

13.jpg

(3)串口接收程序(有BUG,接收两遍才能接收完整)

14.jpg15.jpg

(4)舵机执行动作的程序

采用定时器和标志位,使舵机缓慢打开,一定时间后缓慢自动关闭,4路舵机相互不受影响。

16.jpg

2. CI-C22GS02离线语音模块的命令词、语言模型、声学模型以及固件的制作。具体制作教程建议参考启英泰伦文档中心

(1)命令词列表(根据自己需要定制)

17.jpg

(2)生成的语言模型相应的文件放在这里

18.jpg

19.jpg

(3)生成的声学模型相应的文件放在这里

20.jpg

(4)生成固件之前需要手动修改这里,这里按照数字顺序(16进制)来修改。

21.jpg

(5)合并烧录固件

22.jpg

(6)打包固件

23.jpg

 24.jpg

(7)烧录固件。使用CH340串口工具,CH340的TX接语音模块串口0的RXD,CH340的RX接语音模块串口0的TXD,GNG接GND。CH340先插入电脑,烧录软件中出现对应的串口后,在右边的方框中打勾,然后按住UPDATE 按钮,CH340D的5V接语音模块+5V,此时烧录软件会显示开始烧录,直到烧录完成。

25.jpg

26.jpg

固件烧录成功后,喇叭应该会播报事先录入的欢迎词,这就说明烧录成功。

视频地址:https://www.bilibili.com/video/BV1oh411w7x2/?vd_source=2462ee6bfbc931195...

设计图

27.jpg

原理图_V1.0

28.jpg

原理图_V2.0

29.jpg

PCB_V2.0

工程源文件链接:https://oshwhub.com/myself1820/zhi-neng-yu-yin-fen-lei-de-la-ji-tong

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com

围观 16

无刷电机是一种新型的电机,其特点是不需要刷子进行转子与定子的电接触。相比于传统电机,无刷电机具有更高的效率、更小的体积和更长的寿命。本文将从工作原理、组成结构和与传统电机的区别三个方面来详细介绍无刷电机的特点。

1.png

2.png

图1 网上的无刷电机

首先,无刷电机的工作原理是基于电磁感应和电子控制。无刷电机的转子是由永磁体组成,定子则是由线圈组成。当电流通过定子线圈时,会产生一个磁场,这个磁场会与转子永磁体的磁场相互作用,从而使转子开始旋转。在转子旋转的同时,电子控制器会监测转子的位置和速度,并控制定子线圈的电流,从而保证电机的稳定运行。

其次,无刷电机的组成结构也有所不同。传统电机的转子是由电刷和电极组成,而无刷电机的转子则是由永磁体和磁传感器组成。此外,无刷电机还需要电子控制器来控制电流和转子的位置和速度。这些特殊的组成结构使得无刷电机具有更高的效率和更长的寿命。

最后,无刷电机与传统电机的区别在于其效率、体积和寿命。无刷电机的效率比传统电机高出约10%至20%,这是由于无刷电机不需要电刷进行转子与定子的电接触,从而减少了摩擦和能量损失。此外,无刷电机的体积也比传统电机小,这是由于无刷电机的转子不需要电刷,从而减少了转子的体积。最后,无刷电机的寿命也比传统电机长,这是由于无刷电机不需要电刷进行电接触,从而减少了电刷的磨损和寿命。

3.png

图2 无刷直流电机结构示意图

综上所述,无刷电机具有高效、小巧和长寿命等特点。随着无刷电机技术的不断发展,无刷电机将在越来越多的领域得到应用,如电动汽车、无人机、机器人等。

所以在各个行业运用无刷电机会越来越多,本次会用基于CW32芯片之下开发一款可以应用于大多数场景的一款水泵。现在对于水泵要求越来越高,所以我们改进水泵的工作模式,使水泵工作于最优工作点,降低功耗,就能实现水泵系统的节能,即节约电能和水能,给企业带来效益,同时也能提升竞争力。

在系统中,水泵的运行是靠无刷直流电机来带动的。本课题研究的目的是设计一套基于 CW32 的无位置传感器无刷直流电机控制系统(方波控制模式),既可以改进水泵的工作模式,使水泵工作点最优;又可以使水泵电机结构简单,控制方式更为灵活。以此将整个系统加入各个不同场景中,让CW32低功耗的优点,能让水泵共更加节能,高效。

本文设计一个小型水泵循环控制系统,该系统中带动水泵运行的电机的额定电压是48V,额定功率为500W。控制系统是基于CW32处理器,采用反电势过零检测方法对转子位置进行检测,从而实现对电机的换向控制。水泵控制系统中,采用无位置传感器无刷直流电机来带动水泵运行,不但缩小了水泵体积,而且增强了水泵运行时电机的抗干扰性和稳定可靠性。使水泵工作于最优工作点,尽可能使运行功率降低,改变了水泵一直满功率运行的状况,降低了功耗,实现了水泵系统的节能。

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 39

资料链接如下:

BD网盘链接:

https://pan.baidu.com/s/1IsFqi-bdPQcilu71JMrsVA 

提取码:n99y

1. DS1302是一款集成了实时时钟(RTC)功能的芯片。RTC是一种能够精确测量和跟踪时间的电子设备,可以提供准确的日期和时间信息。DS1302实时时钟芯片由Maxim Integrated(美信半导体)生产,具有以下主要特点:

①时钟精度:DS1302采用32.768kHz的晶体振荡器来提供基准时钟信号。它具有较高的时钟精度,使得它可以提供准确的时间信息,每月的时间偏差非常小。

②时钟和日历功能:DS1302可以跟踪秒、分钟、小时、日期、月份和年份。它提供了完整的时钟和日历功能,可以准确地记录时间和日期。

③低功耗设计:DS1302芯片采用了低功耗设计,因此在电池供电时可以提供长时间的运行时间。

④数据保持功能:DS1302具有数据保持功能,使得即使在电源断电的情况下,时钟和日历数据仍然可以得到保持,确保数据的可靠性和连续性。

⑤简化的接口:DS1302通过串行接口(2线或3线),与微控制器或其他外部设备进行通信。这种简化的接口使得它更容易与其他系统集成,实现实时时钟功能。

DS1302实时时钟芯片被广泛应用于各种应用领域,如电子设备、仪器仪表、自动化系统、安防系统等需要准确时间信息的场合。它的易用性、低功耗以及稳定的性能使得它成为常用的实时时钟解决方案之一。

2.本实验用到了CW32 Cortex-M实训套件实验箱、DS1302实时时钟模块及Keil5开发环境。

1.png

CW32 Cortex-M实训套件

2.png

DS1302实时时钟模块实物

3.png

DS1302原理图

3.DS1302具体工作原理可观看视频,或参考以下链接中的文章

https://blog.csdn.net/u013184273/article/details/78843881

4.核心代码

/*
用TFT屏幕显示年、月、日、星期、时、分、秒
显示界面如下:
----------------
DS1302 Real Time 

Year :xxxx
Month:xx
Day  :xx
Week :xx
xx:xx:xx

----------------
*/

#include "main.h"
#include "config.h"
#include "Ds1302.h"        //DS1302底层驱动代码
#include "Lcd_Driver.h"
#include "LCD_calculate.h"

char strings[2];         //储存要显示的数字

int main()
{  
    u8 value = 0;   
    
    RCC_Configuration(); //配置时钟   
    
    Lcd_Init();  
    Lcd_Clear(GRAY0);   
    
    Ds1302_GPIO_Init();  //GPIO配置 
    
    //  Ds1302_Config(23,7,7,5,14,58,55); //初始化DS1302,2023年7月7日星期五23点59分55秒   

    Gui_DrawFont_GBK16(0,0,WHITE,RED,"DS1302 Real Time"); //显示界面  
    Gui_DrawFont_GBK16(0,32,WHITE,RED,"Year :20  ");  
    Gui_DrawFont_GBK16(0,48,WHITE,RED,"Month:    ");  
    Gui_DrawFont_GBK16(0,64,WHITE,RED,"Day  :    ");  
    Gui_DrawFont_GBK16(0,80,WHITE,RED,"Week :    ");  
    Gui_DrawFont_GBK16(0,96,WHITE,RED,"  :  :    ");   

    while(1)  
    {        
        Ds1302_GetHour(&value);                  //小时    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(0,96,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(8,96,WHITE,RED,strings);     
    
        Ds1302_GetMinite(&value);                //分钟    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(24,96,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(32,96,WHITE,RED,strings);     
        
        Ds1302_GetSecond(&value);                //秒钟    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(48,96,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(56,96,WHITE,RED,strings);       
    
        Ds1302_GetDay(&value);                  //星期    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(40+8,80,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(48+8,80,WHITE,RED,strings);     
    
        Ds1302_GetDate(&value);                 //日期    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(32+16,64,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(40+16,64,WHITE,RED,strings);    
       
        Ds1302_GetMonth(&value);                //月份    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(48,48,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(55,48,WHITE,RED,strings);     
    
        Ds1302_GetYear(&value);                 //年份    
        sprintf(strings,"%d",value/10);    
        Gui_DrawFont_GBK16(56+8,32,WHITE,RED,strings);    
        sprintf(strings,"%d",value%10);    
        Gui_DrawFont_GBK16(64+8,32,WHITE,RED,strings);  
    }
}

5.实验最终现象

把程序下载进DS1302模块,DS1302经过程序初始化后将开始计时。

4.png

实验箱接线 

5.png

显示时间   

 6.png

断电30s重新上电  

来源:CW32生态社区

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 6

页面

订阅 RSS - CW32