CW32

智能科技的迅速发展使得我们的日常生活变得更加便捷和舒适。智能马桶作为其中一种智能家居产品,通过单片机接受和处理来自传感器的数据,然后通过控制模块对智能马桶的各项功能进行控制,实现对智能马桶的全面控制和调节。本文将介绍CW32单片机在智能马桶的详细应用。

1.png

图:CW32的智能马桶控制板

CW32单片机在智能马桶的应用介绍

1.温度感应与控制

智能马桶内设有温度传感器,通过CW32单片机的处理,可以实时感知到用户所需的座圈温度并根据用户的设定进行调节。当温度过高或过低时,单片机将发出信号,通过控制模块对座圈的加热或制冷进行调节,提供更加舒适的使用体验。

2.png

图:CW32在智能马桶控制板的应用框图

2.智能冲洗功能控制

CW32单片机通过接收用户的操作指令,进行流量检测,根据用户所需的水流大小和冲洗时间,精确地控制水泵的工作和停止。

3.智能电量监控与节能功能

智能马桶内置电池或电源模块,CW32单片机可以实时监测电池的电量,并根据电量情况进行智能的调节。当电量不足时,单片机会通过控制模块对电源进行调节,以延长电池的使用寿命。例如,降低加热功率或减少冲洗时间等,通过智能的电量管理,实现了对电能的高效利用和节约。

这款智能马桶采用的是武汉芯源半导体32位低功耗CW32L083系列,该系列产品集成了主频高达64MHz的ARM® Cortex®-M0+ 内核、最多256KB FLASH 、最多 24KBRAM、最多87路GPIO,以及一系列增强型外设。

CW32L083系列产品优势介绍

1、系统特性

● ARM® Cortex®-M0+ 内核;

● 最高主频64MHz;

● 温度范围:-40℃ 至 85℃;

● 宽压供电:1.65V 至 5.5V;

2、高可靠性

● HBM ESD 8KV;MM ESD、CDM ESD、Latch up@105℃全面达到JEDEC较高等级;

● EFT 4KV,IEC61000-4-4 Class:4(Power)/4(IO);

● 支持奇偶校验;

● 符合IEC60730,IEC61508 功能安全设计规范;

3、超低功耗

● 支持0.6uA@3.3V深度休眠模式;支持1.7uA@3.3V深度休眠模式+IWDT工作;

● 深度休眠模式下电流0.6uA,超低功耗唤醒时间仅需4us;

● 支持灵活的中断唤醒及事件唤醒;

4、内部资源

● 最多87路GPIO;

● 1路12位ADC;

● 六路低功耗 UART,支持小数波特率;

● 两路 SPI 接口 12Mbit/s;

● 两路 I2C 接口 1Mbit/s;

● 16 位高级控制定时器;四组 16 位通用定时器;三组 16 位基本定时器;一组 16 位超低功耗定时器;窗口看门狗定时器;独立看门狗定时器;

● 1路低功耗定时器(LPTIM)

● 4×56、6×54 或 8×52 LCD 段码液晶驱动器

● TRNG 真随机数发生器

● AES 高级加密标准模块

CW32L083系列产品适用于对FLASH、RAM、GPIO等资源需求较大,且有LCD显示、低功耗要求的应用场合,该系列目前可提供LQFP64、LQFP80、LQFP100三种封装形式。

3.png

图:CW32L083系列封装展示

CW32L083系列产品非常适合各种小、中型电子产品的应用领域,比如医疗和手持设备、PC外围设备、游戏设备、运动装备、报警系统、智能门锁、有线和无线传感器模块、表计等产品。

来源: 武汉芯源半导体

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

围观 19

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

BD网盘链接:

https://pan.baidu.com/s/1lJbXZsBnEBjsz5bCfKRoQA?pwd=kq5a 

提取码:kq5a

一、实验简介

基于CW32单片机的门禁综合系统是一款功能强大的智能门禁解决方案,它提供了三种开锁方式:密码开锁、指纹开锁和刷卡开锁,为用户提供方便、安全的门禁控制。该系统的核心控制器采用了CW32单片机,具备高性能和可靠性,能够稳定地运行门禁系统的各项功能。

密码开锁是最常见的开锁方式之一。用户可以通过系统提供的密码输入界面,输入正确的密码来解锁门禁系统。这种方式简单明了,适用于多种场景。

指纹开锁是一种更加安全、个性化的开锁方式。系统可以保存多个用户的指纹信息,用户只需将手指放在指纹识别器上进行验证,合法的指纹即可解锁门禁系统。这种方式便于用户快速、准确地开锁,并且具有很高的安全性,因为指纹信息是独一无二的。

刷卡开锁是另一种常见的开锁方式。系统配备了IC卡读卡器,用户只需通过刷卡即可进行开锁。IC卡可以根据实际需求进行添加和删除,非常方便管理和控制用户的权限。

除了这三种开锁方式,该系统还提供了自主修改密码和添加删除指纹、IC卡的功能。管理员可以通过系统设置界面轻松地修改密码,添加或删除用户的指纹和IC卡信息,确保安全性。

二、实验器材

本实验使用到了CW32-48F大学计划开发板、RC522刷卡模块、AS608指纹模块、继电器、薄膜键盘及Keil5开发环境。

1.png

CW32-48F大学计划板    

2.png

RC522刷卡模块

3.png

AS608指纹模块

4.png

继电器模块

5.png  

薄膜键盘

6.png

实物图

三、接线

开发板上预留了矩阵键盘接口

 7.png

【RC522刷卡模块与单片机接线】:

VCC  --  3.3V

GND  --  GND

RST --  PB9

SCK --  PA11

SDA --  PA10

IRQ --  ×

MOSI -- PA12

MISO -- PA15

【AS608指纹模块与单片机接线】:

红线 -- 3.3V

黄线 -- PA3

黑线 -- GND

绿线 -- 3.3V

蓝线 -- PC15

【继电器模块与单片机接线】:

VCC -- 3.3V

GND -- GND

OUT -- PB1

四、核心代码

main.c:
/*函数声明*/
void KEY_Proc(void);                  //按键处理函数
void RC522_Proc(void);                //刷卡处理函数
void AS608_Proc(void);                //指纹处理函数
void System_Init(void);               //系统初始化函数
void Passport_Proc(void);             //密码处理函数
void System_Reload(void);             //系统重置函数
void Mode_Interface(void);            //模式切换界面
void Work_Interface(void);            //工作界面
void Buzzer_Ring(uint8_t ms);         //蜂鸣器函数
_Bool Indentity_Verify(void);         //身份验证函数
void Init_MyStruct(id* mystructure);  //结构体初始化函数

/*主程序*/
int main()
{  
    System_Init();        //系统初始化  
    Mode_Interface();     //上电进入模式选择界面
   
   while(1)  
   {    
       KEY_Proc();       //按键处理    
       RC522_Proc();     //刷卡处理    
       AS608_Proc();     //指纹处理    
       Passport_Proc();  //密码处理  
   }
}

/*子程序*/
void System_Init(void)                //系统初始化函数
{  
    __NVIC_SetPriority(BTIM2_IRQn,0); //BTIM2高优先级  
    __NVIC_SetPriority(BTIM1_IRQn,1); //BTIM1低优先级    
      Lcd_Init();                       //屏幕初始化    
      BTIM1_Init();                     //定时器初始化  
    BTIM2_Init();    
      Relay_Init();                     //继电器初始化  
    RC522_Init();                     //刷卡模块初始化  
    Matrix_Init();                    //薄膜键盘初始化  
    Buzzer_Init();                    //蜂鸣器初始化  
    Lcd_Clear(BLACK);                 //清屏    
      UART2_Init(57600);                //AS608指纹模块串口初始化    
      Init_MyStruct(&card);             //卡片结构体初始化  
    AS608_WAK_GPIO_Init();            //AS608手指感应初始化
}

void Mode_Interface(void)            //用户\管理模式界面
{      
    Lcd_Clear(BLACK);  
    TFT_User();                      //用户模式界面  
    while(start==0)  
    {      
        if(Key_Scan()!='.')    
        {      
            KEY_Proc();      
            if(Mode==0)                      
                TFT_User();          //用户模式界面      
            else if(Mode==1)                 
                TFT_Administrator(); //管理模式界面    
        }  
    }    
    method=0;                        //解锁模式0:密码解锁  
    TFT_Passport(Mode);              //进入用户模式的密码界面
}

void Work_Interface()                //工作界面
{  
    if(method==3)                    //三种解锁模式循环显示    
        method=0;  
    else if(method>3)    
        method=2;
        
   if(method==0)    
       TFT_Passport(Mode);          //密码解锁  
   else if(method==1)    
       TFT_Fingerprint(Mode);       //指纹解锁  
   else if(method==2)    
       TFT_Card(Mode);              //刷卡解锁
}

void KEY_Proc(void)                  //按键处理函数
{  
    if(!exert_flag)                       
        return;  
    temp=Key_Scan();                 //读取键值  
    exert_flag=0;  
    if(temp=='.')      
        return;
    
    switch(temp)  
    {    
        case 'F':  if(start) return; Mode=0; break;  //用户模式                                                
        case 'f':  if(start) return; Mode=1; break;  //管理模式        
            case '#':  if(Mode==0) start=1; else {if(Indentity_Verify()==0) start=1;} break; //开始工作                    
        case '*':  if(!start) return; start=0; System_Reload();    break;  //系统重置                
        case '(':  if(!start) return; method-=1; Work_Interface(); break;  //切换解锁方式    
        case ')':  if(!start) return; method+=1; Work_Interface(); break;  //切换解锁方式                     
        case '+':  if(start==0) Tips_Open=1; break;  //打开信息提示                 
        case '-':  if(start==0) Tips_Open=0; break;  //关闭信息提示      
    }
}

void Passport_Proc(void)               //密码处理
{      
    uint8_t i=0;  
    uint8_t result=0;  
    char uc_temp='.';
   
   if(method!=0) {idx=0; return;}              //解锁模式0:密码解锁  
   if(Passport_Input(uc_passport,PASSPORT_LENGTH)==0) return;  //判断密码输入是否完成   
 
   if(Mode==0)                                 //用户模式  
   {    
       for(i=0;i<PASSPORT_LENGTH;i++)      
           result+=uc_passport[i]^passport[i]; //使用异或判断输入的密码与设置的密码是否一致    
       if(result==0)                           //密码正确    
       {      
           Relay_Flag=1;                       //继电器标识打开      
           PB01_SETHIGH();                     //开锁      
           TFTShowString(7,0,"               ");      
           TFTShowString(8,4,"Unlocked");    
       }    
       else                                    //密码错误    
       {      
           TFTShowString(7,0,"                ");      
           Gui_DrawFont_GBK16(5,128,WHITE,BLACK,"     Error     ");      
           Buzzer_Ring(60);                    //错误提示音      
           Delay_ms(500);      
           TFTShowString(8,0,"                ");     
       }  
   }  
   else                                        //管理模式  
   {    
       TFTShowString(5,2,"Esc: concel ");    
       TFTShowString(7,2,"Ent: comfirm");    
       while(1)                                //等待按键输入(确认/取消)    
       {      
           uc_temp=Key_Scan();      
           exert_flag2=0;      
           if(uc_temp=='R')                    //键入:取消      
           {        
               Buzzer_Ring(120);               //操作提示音        
               TFTShowString(5,2,"              ");        
               TFTShowString(7,2,"              ");        
               TFTShowString(6,2," set concel   ");        
               Delay_ms(500);        
               TFTShowString(6,2,"              ");        
               break;      
           }      
           else if(uc_temp=='E')               //键入:确认      
           {                
               Buzzer_Ring(120);               //操作提示音        
               TFTShowString(5,2,"              ");        
               TFTShowString(7,2,"              ");        
               memcpy(passport,uc_passport,sizeof(uc_passport)); //覆盖开锁密码        
               TFTShowString(6,2,"set  succeed  ");        
               Delay_ms(500);        
               TFTShowString(6,2,"              ");                
               break;      
           }    
       }    
   }
}

void AS608_Proc(void)                     //指纹处理
{    
    char uc_temp='.';   
    
    if(method!=1)  return;                //解锁模式1:指纹解锁       
    
    if(Mode==0)                           //用户模式  
    {    
        if(AS608_PressTest()==0)          //按下    
        {      
            if(AS608_MatchTest()==0)      //匹配      
            {        
                PB01_SETHIGH();           //开锁        
                Relay_Flag=1;        
                TFTShowString(8,4,"Unlocked");      
            }      
            else if(!Relay_Flag)         
            {        
                TFTShowString(7,0,"                ");        
                Gui_DrawFont_GBK16(3,128,WHITE,BLACK,"          Error     ");        
                Buzzer_Ring(60);          //错误提示音        
                Delay_ms(500);        
                TFTShowString(8,0,"                ");      
            }    
        }    
    }  
    else  
    {      
        TFTShowString(7,3,"Esc: Delet ");    
        if(!exert_flag2) return;    
        uc_temp=Key_Scan();    
        exert_flag2=0;        
        if(AS608_AddTest(Finger_ID)==0)       //添加指纹    
        {      
            Buzzer_Ring(120);                 //执行成功提示音      
            TFTShowString(6,1," Add succeeded");      
            Delay_ms(500);      
            TFTShowString(6,0,"                ");    
        }     
        
        if(uc_temp<=validFinger_num)          //键入:id       
        {      
            Finger_ID=uc_temp;                      
            TFT_Fingerprint(MANAGE_MODE);    
        }    
        else if(uc_temp=='R')                 //键入:删除    
        {      
            if(PS_DeletOneChar(Finger_ID)==0) //删除      
            {        
                Buzzer_Ring(120);             //执行成功提示音        
                TFTShowString(6,1,"Delet succeeded");        
                Delay_ms(500);        
                TFTShowString(6,0,"                ");      
            }    
        }  
    }   
}

void RC522_Proc(void)                         //刷卡处理
{    
    char uc_temp='.';  
    uint8_t uc_uid[4]={0};  
             
   if(method!=2)  return;                    //解锁模式2:刷卡解锁     
   
   if(Mode==0)                               //用户模式  
   {    
       if(RC522_ReadIDTest(uc_uid)==0)       //识别到卡    
       {      
           if((memcmp(card.uid0,uc_uid,sizeof(uc_uid))==0)  ||  //检索ic序列号           
           (memcmp(card.uid1,uc_uid,sizeof(uc_uid))==0)  ||         
           (memcmp(card.uid2,uc_uid,sizeof(uc_uid))==0)        
           )      
           {        
               PB01_SETHIGH();               //开锁        
               Relay_Flag=1;               
               TFTShowString(8,4,"Unlocked");      
           }      
           else       
           {        
               TFTShowString(7,0,"                ");        
               Gui_DrawFont_GBK16(0,128,WHITE,BLACK,"      Error     ");        
               Buzzer_Ring(60);              //错误提示音        
               Delay_ms(500);        TFTShowString(8,0,"                ");      
           }    
       }  
    }  
    else  
    {    
        TFTShowString(7,3,"Esc: Delet ");    
        if(!exert_flag2) return;        
        uc_temp=Key_Scan();    
        exert_flag2=0;    
        if(uc_temp<validCard_num)            //键入:id    
        {      
            Card_ID=uc_temp;          
            TFT_Card(MANAGE_MODE);    
        }    
        if(Card_ID==0)                         
        {      
            if(uc_temp=='R') memset(card.uid0,0,sizeof(card.uid0)); //删除ic卡0序列号      
            else if(RC522_ReadIDTest(card.uid0)!=MI_OK) return;     //添加ic卡0序列号     
        }        
        else if(Card_ID==1)    
        {      
            if(uc_temp=='R') memset(card.uid1,0,sizeof(card.uid1)); //删除ic卡1序列号            
            else if(RC522_ReadIDTest(card.uid1)!=MI_OK) return;      //添加ic卡1序列号        
        }        
        else if(Card_ID==2)    
        {      
            if(uc_temp=='R') memset(card.uid2,0,sizeof(card.uid2)); //删除ic卡2序列号            
            else if(RC522_ReadIDTest(card.uid2)!=MI_OK) return;     //添加ic卡2序列号        
        }     
    
        if(uc_temp=='R') TFTShowString(6,1,"Delet succeeded");    
        else        TFTShowString(6,1," Add succeeded");    
        Buzzer_Ring(120);    
        Delay_ms(500);                       //执行成功提示音    
        TFTShowString(6,0,"                ");     
    }
}

void System_Reload(void)   //重置系统,恢复到上电默认的状态
{  
    idx=0;   
    Mode=0;                   
    start=0;  
    method=0;  
    while(Relay_Flag);   
    Lcd_Clear(BLACK);  
    Mode_Interface();
}

来源:CW32生态社区

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

围观 11

前言

人们户外露营、手机终端等方面的用电需求日益明显,便携式储能设备成为理想的选择。伴随着便携式储能市场的持续增长,便携锂电BMS应用空间也会逐渐扩大。虽然目前便携锂电BMS在研发设计中面临着诸多挑战,但正因为这些挑战,让其成为企业快速抢占市场的重要条件。便携式储能设备需求增大,那么便携锂电BMS市场发展可期可待,轻量化、定制化、安全快充的市场需求也会成为未来的发展方向。

现在,市场上各类需要配备锂电池、且要求快速充电的电子产品越来越多。例如:便携类仪器仪表(配置2~6节锂电池)、便携式产品锂电池能量包、支持快充快放的移动电源充电宝等。

现在外面很多充电宝都是千篇一律的四个电量指示灯和一个唤醒按键,显示的信息不全,电量指示显示得也不准。所以我们设计了一款带屏幕的充电宝。在屏幕上能精确的显示充电宝的电量、充电状态、输出电压、输入电流等信息。这样的设计可以提供更直观、方便的使用体验。并且我们开源这款充电宝,让各位电子爱好者能DIY出符合自己个性的充电宝。

方案综述

本项目以CW32F003F4P7为核心,配合英集芯的充电管理芯IP5389,设计了一个完整的移动电源系统,具有彩屏显示充放电的电压、电流、功率等状态,具有过温保护、超温自动降额、过流、过压等完善保护措施。支持各种协议的快速充电,以及大电流快速放电。支持充电/放电电压5~22V,最大充放电功率100W(最终充放电指标由充电设备及用电设备的标准而确定)。支持2~6节串联锂电池组的快速充放电。

此方案不但适合高端移动电源设计参考,而且对所有便携式产品锂电池能量包(BMS)方案的用户,都有非常直接的帮助作用,可以为用户节省1~3月的开发时间。

方案优势:方案基于CW32的20 PIN的MCU设计,具有5V系统、强健抗干扰能力、超低功耗、宽温度(105度)等特性,相比ST或其他国产方案,价格更有优势,性能更稳定。CW32 MCU与IP5389的配合,让方案应用特别灵活多变,特别适合新兴的便携锂电BMS市场的方案需求。

适合行业:移动电源、户外电源控制、新能源、大功率智能电动工具电池包、便携式产品电池包BMS、便携锂电BMS市场(新兴市场)。特别适合用于要求有快速充放电、兼容各种充放电协议的新兴便携锂电BMS方案的应用:DC输入充电(5~20V)、TYPE输入充电(PD协议,5~20V,MAX 100W),输出USB、TYPE或普通供电输出要求(可配合诱骗芯片)。

硬件方案

本方案采用英集芯IP5389作为协议和21700电芯管理芯片,选择市面上便于采购的公模外壳进行安装。同时因为体积考虑,将充放电功率限制在了45W及以下。

1.png

2.png

BOM

3.png

软件编写

首先我们需要根据本充电宝的功能来确定我们所需要使用到的外设。需要与IP5839通讯来读取寄存器,将读取的电压电流信息以及电池温度、MCU内部温度显示在LCD屏幕上,同时为了降低充电宝的待机功耗,我们还需要在待机时将MCU设置为低功耗模式。

屏幕显示 spi外设

读取IP5839的寄存器 I2C

低功耗

读取电池温度与MCU温度 ADC模块

按键唤醒充电宝 GPIO

使用定时器作为系统软件时基单元。

底层驱动部分

开源链接

详细项目文档及资料获取请阅览飞书文档:

https://dwi41yhz703.feishu.cn/docx/VxMGdmu9jo9D5txVdPocJwlrney?from=from_copylink

硬件开源链接(立创):

https://oshwhub.com/beauty_light/cw32-intelligent-charging-treasure-scheme

来源:CW32生态社区

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

围观 38

上回说到单片机的Uart发送,我们编写了一个发送函数循环发送固定的字符串,这回我们讲Uart的中断接收功能。

说一下中断是什么,大概就是说,单片机只有一个核,就是只有一个大脑,他无法一核二用地做事,但有时候迫不得已需要去响应一些紧急的命令,就好比你打游戏开团了,你妈喊你去倒开水,倒开水就会触发咱们人类的“中断”功能。放在单片机上,进行中断操作需要以下几个条件和步骤:拥有中断源、中断控制器正常工作、触发中断、保护现场、响应中断、恢复现场。

看名字可能会比较抽象,我来具体解释一下。

中断源,单片机上会有很多的中断源,也就是有很多办法、或者说“渠道”去触发中断,Uart外设就有很多触发中断的办法,而我们本文涉及到的就是接收信息会触发的中断,具体怎么触发的,后文会详细解释。

中断控制器,这个东西是一个物理存在于单片机内核里面的一块数字电路,这一块电路的功能就是用来管理中断的。对于一些老旧型号的单片机,比如C51单片机,他内部也有这个东西,只不过其中断优先级是固定的,这个控制器只扮演了“总闸”这样的角色。再看CW32这种32位单片机,使用cortex-M0+内核,拥有可编程的中断控制器,单片机上会有很多个中断源,但这是内核可以使用和管理的部分,芯片制造厂使用这一款内核制造单片机,并不会用到所有的中断资源,不只是搭载的功能有限,还受限于封装,很多中断资源会被闲置。但是只要使用芯片的中断,都必须正确配置内核里面的中断控制器,否则中断是无法工作的,因为不论单片机外设设计的如何天花乱坠,外设只负责触发中断,而响应中断的一定是内核。

中断的触发,前面提到了中断源,一个指定的中断只能由特定的、与其绑定的中断源触发,一个中断可能绑定多个中断源,但是只会有一个与中断绑定的中断服务函数,至于什么是中断服务函数,后文会解释。那这个时候肯定会有读者问了“那单片机如何在一个中断里面区分不同的中断源呢?”,单片机对不同的中断源,都设计了中断标志位,假设有ABC三个中断源,那他们就对应了3个标志位(3比特位),没触发中断的时候,ABC的中断标志位就是默认值0,如果触发中断,电路硬件会对其对应的标志比特位进行置位操作,也叫置1操作,该比特位会变成1。这个置位行为会直接反馈到内核的中断控制器,随后内核会对中断信号进行响应。

保护现场,看名字似乎和编程关系不大,这个名词在教科书上的中断章节会高频出现。我们无法预测中断会在什么时候到来,CPU也不能一直傻傻地等中断到来,所以不需要响应中断的时候,CPU还是照常工作的。想象现在CPU正在执行一个函数function(),倘若函数还未执行完成,中断被触发,CPU应该怎么做?是放下function函数不管不顾直接去响应,抑或是先做点什么?显而易见,后者更好更合理,需要做的,正是保护现场,函数执行到哪一步,CPU就会把执行到这一步的CPU数据(不只是我们要看的数据,还包括了程序执行的情况)存放到堆栈中,在中断响应完成之前,这些数据都会被封存,以避免响应完成后数据的丢失。

响应中断,这个是大部分人最关心的部分,因为这个部分直接涉及到中断服务函数的编写。在一切准备就绪后,CPU会放弃下一条需要执行的语句并直接进入中断服务函数,这里需要理解“中断服务函数”它仍然是个函数,初学者可能会认为,C语言的函数需要调用才会被执行,这里没被调用却被执行了,那肯定不是函数。实际上看过单片机原理或者了解过计算机原理的小伙伴会告诉你,CPU内部会有一个程序指针,程序指针会按照代码编译之后的逻辑去依次指向需要被执行的函数,单片机进入中断服务函数的原理就是直接设置这个指针指向中断服务函数,之后CPU就能执行中断代码响应中断了。

恢复现场,对应于保护现场,CPU必须在响应中断之后回到之前被中断打断的语句那里继续执行,取出原路堆栈中的数据就完成了恢复。

掌握中断相关的知识后,我们就可以自己编写和中断相关的代码了,编写程序时,基本上只需要注意中断标志位、中断服务函数、中断控制器就可以,保护现场什么的单片机会自己完成。

在包含了必要的头文件之后,在初始化函数中加入下图的代码即可完成对中断控制器的设置:

1.png

第一行和第二行的函数均是对内核里的中断控制器进行寄存器操作。

解释一下第二行的设置中断优先级,这里涉及到一个中断嵌套的概念,中断不会只有一个,并且很有可能下一个中断触发的时候,上一个中断还没有执行完,此时就需要严格设置中断优先级,在单片机中,根据内核用户手册,优先级从0开始递增,优先级数字越低,其优先级越高,高优先级中断可以直接打断低优先级中断的响应,立刻响应高优先级中断,形成中断嵌套,这里设置为1是因为这个回发功能不算很重要的功能,相比之下嘀嗒定时器会为单片机程序提供时基信号,其优先级应该更高。关于优先级的具体解释,可以进行网上搜索或是查看《cortex-M0+内核手册》。

关于最后一行代码,CW_UART1这个外设拥有很多个中断源,这些中断源的使用是独立的,这里只使用了接收中断这一个中断源,芯片手册的通用异步收发器章节展示了Uart中断包含的中断源。

2.png

当有数据进入单片机的Uart1接收缓冲区时,接收中断会触发,中断标志位置1,程序跳转至Uart1的中断服务函数。单片机几乎所有的中断服务函数都会由一个单独的文件收录,名为interrupt_xxxx.c或者xxxx_it.c。这里贴一张简易的中断服务函数代码,其功能是在尽量不破坏单片机实时性的情况下把数据放入一个既有的数组。

 3.png

前文有提到,硬件会根据中断标志位决定是否进入中断服务函数,如果不在中断服务函数中清除中断标志位,单片机就会反复进入中断,导致程序死在中断里。

说一下代码的思路,len是一个变量,是缓冲区内非空数据的个数;data_rx是一个字符数组,作为接收缓冲区,缓冲区大小为200;进入中断之后首先判断缓冲区是否还有位置,也就是len是否超出缓冲区数组下标上限,超出则判定为缓冲区已满,丢掉后续所有的数据直到缓冲区有空位;变量 Rx_Flag是一个8位无符号数,作为缓冲区有数据&缓冲区满的标志位使用;对于接收的所有数据,均会判断是否是“\r\n”,这个字符串在编码中是换行符,只要判断到最近接收的两个字节数据是连续的0X0D和0X0A,就认定接收到换行符,本次数据接收完毕,Rx_Flag置1表示完成一次完整的数据接收。

需要注意的是,中断的响应并非一个非常可靠的函数调用,一些编译器会试图优化掉代码对某些变量的修改操作(他们可能察觉不到中断函数的存在而认为变量不需要被修改),因此需要在中断中修改的变量需要加上“volatile”关键字以防止对变量的操作被编译器优化。 

4.png

到目前位置,数据其实已经被保存在数组data_rx里面了,但这段数据我们从外部是看不到的,也看不到是否是我们设想的功能完成的接收,所以我编写了如下函数,此函数可以在Uart1完成了一次完整的数据接收(Rx_Flag置1)后立刻回发接收的数据,并清空接收缓冲区,允许进行下一次接收。

5.png

因为函数包含发送功能,所以保留了超时跳出的保险措施。这里解释一下time_ms这个变量的作用,该变量定义在嘀嗒定时器文件中,并在嘀嗒定时器中断服务函数中递增1,即每1ms该变量都会增加1,作为毫秒计数值使用,本系列教程大部分实时性较弱的功能都会依赖此功能进行定时。如有疑问可以移步《内核外设-嘀嗒定时器》章节学习。

在轮询中加入这个回发函数,最大发送容忍时间100ms,并设置间隔1000ms发送一次“success”+“换行符”。随后在串口助手中发送不超过200字节的文本数据,即可验证接收是否成功。

6.png

7.png

看来单片机顺利接收了数据并进行了回发操作,本节完。

总结:

1.注意理解中断的概念;

2.同一个中断可能会有多个中断源;

3.中断的执行不可靠,中断内涉及到修改的变量需要加上volatile防止优化;

4.串口的每一次发送携带很少的数据量,因此非常建议使用缓冲区来接收数据,待需要时再主动读取;

来源: CW32生态社区

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

围观 14

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

BD网盘链接:

https://pan.baidu.com/s/1YZJtAhO3Wsjy5aBuTJ-c_Q?pwd=rm7q \

提取码:rm7q

一、实验简介

1.公交站人流检测是一项重要的城市交通管理任务,它不仅关系到公交服务的效率和质量,还与城市交通规划和公共安全紧密相关。首先,公交站人流检测可以实时反馈乘客流量情况,帮助公交公司了解各线路、各站点的客流需求。其次,公交站人流检测可以为公交公司制定和优化运营策略提供数据支持。再次,公交站人流检测可以及时发现拥挤、拥堵等情况,提醒调度中心及时采取措施,保障乘客安全。基于此背景,我们以CW32单片机为核心,采用公交站人流检测中常见的红外线检测技术,设计了一个公交站人流检测系统,该系统可以连接到网络获取当前时间,并将人流数据实时上传到OneNET云平台进行远程监控。

二、实验器材

本实验使用到了CW32-48F大学计划开发板、ESP8266WIFI模块、E18-D80NK红外光电开关及Keil5开发环境。

1.png

CW32-48F大学计划板 

2.png

ESP8266WIFI模块

3.png

E18-D80NK红外光电开关

4.png

E18-D80NK红外光电开关

5.png

实物图

开发板上预留了ESP8266WIFI模块接口,通过串口与ESP8266通信。

 6.png

【两个红外光电开关与单片机接线】:

蓝线 -- GND

棕线 -- 3.3V

黑线 -- PA0、PA4

三、核心代码

main.c:
//单片机头文件
#include "main.h"

//网络设备
#include "esp8266.h"

//网络协议层
#include "onenet.h"     //OneNET

//硬件驱动
#include "RTC.h"        //CW32RTC
#include "BTIM.h"
#include "usart.h"
#include "Buzzer.h"     //板载蜂鸣器
#include "Infrared.h"   //红外光电开关
#include "Lcd_Driver.h"
#include "LCD_calculate.h"

uint8_t send_flag=0;    //数据上云控制位
uint16_t count=0,accumulation=0;  //count--当前站内人数,accumulation--累计人数
enum State{  
    STATE_A,  
    STATE_B,  
    STATE_C,  
    STATE_D,
};         //枚举数据类型,包含四个状态

void System_Init(void)
{  
    Lcd_Init();          //LCD屏初始化  
    BTIM_Init();         //基本定时器初始化,调控数据上云频率  
    Buzzer_Init();       //蜂鸣器初始化,两个红外光电开关同时被遮挡时发出声响警告  
    Infrared_Init();     //红外光电开关初始化  
    Usart1_Init(115200); //串口1,用于串口调试助手打印调试信息  
    Usart2_Init(115200); //串口2,与ESP8266进行通信  
     
    TFT_Welcome();     //开机界面显示  
    Gui_DrawFont_GBK16(0,128,GRAY1,WHITE,"   网络连接中  ");  
    ESP8266_Init();//联网,获取当前时间,接入OneNET云平台  
    RTC_ITConfig(RTC_IT_INTERVAL,ENABLE); //开启RTC周期中断(RTC中断开启的时机要在esp8266连接到onenet平后之后)  
    Gui_DrawFont_GBK16(0,8,WHITE,BLUE," 公交站人流检测 ");  
    Gui_DrawLine(0,32,128,32,GRAY1);  
    Gui_DrawFont_GBK16(0,64,WHITE,BLUE," 站内人数:");  
    TFTShowNumber(4,12,count);  
    Gui_DrawFont_GBK16(0,96,WHITE,BLUE," 累计人数:");  
    TFTShowNumber(6,12,accumulation);  
    NVIC_EnableIRQ(BTIM1_IRQn);    //开启定时器中断,中断周期10ms
}

int main(void)
{  
    uint16_t state=0xffff;            //红外光电开关当前状态  
    enum State currentState=STATE_A;  //开机为状态A   
    System_Init();                    //系统初始化  
    while(1)  
    {        
        /*模拟乘客进站的过程:      
            1.装置安装位置:两红外光电传开关一前一后安装在站口      
            2.假设站口只允许乘客排队依次进出      
            3.乘客进站先遮挡红外光电开关1      
            4.乘客继续前进,离开红外光电开关1,遮挡红外光电开关2      
            5.站内人数+1,累计人数+1(若过程4中同时遮挡两个红外光电开关则蜂鸣器发出警报,等待工作人员解决故障,不计数)    
        */    
        /*模拟乘客出站的过程:      
            1.装置安装位置:两红外光电传开关一前一后安装在站口      
            2.假设站口只允许乘客排队依次进出      
            3.乘客出站先遮挡红外光电开关2      
            4.乘客继续前进,离开红外光电开关2,遮挡红外光电开关1      
            5.站内人数-1(若过程4中同时遮挡两个红外光电开关则蜂鸣器发出警报,等待工作人员解决故障,不计数)    
        */    
        state=CW_GPIOA->IDR & 0x0011;   //获取两个红外光电开关数据IO当前状态    
        if(state==0x0000) Buzzer_RING;  //控制蜂鸣器     
        else Buzzer_OFF;    
        switch(currentState)            //初步模仿状态机编程模式    
        {      
            case STATE_A:        
                if(state==0x0011)      currentState=STATE_B;   break;        
            case STATE_B:        
                if(state==0x0001)      currentState=STATE_C;          
                else if(state==0x0010) currentState=STATE_D;   break;      
            case STATE_C: if(state==0x0010) {count++;accumulation++;currentState=STATE_A;TFTShowNumber(4,12,count);TFTShowNumber(6,12,accumulation);}  break;      
            case STATE_D: if(state==0x0001) {if(count!=0) count--;  currentState=STATE_A;TFTShowNumber(4,12,count);}                        break;    
        }    
        if(send_flag)    
        {          
            OneNet_SendData();  //数据上传到OneNET      
            ESP8266_Clear();    //清除缓存      
            send_flag=0;      
        }  
    }
}

四、效果演示

7.png

连接网络

8.png

数据显示

来源:CW32生态社区

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

围观 33

本章介绍CW32通用异步收发器的使用。

虽然大部分时候会被简单称为“串口”,但实际上用“串口”来指代UART并不正确。串口对应并口的概念,在串口通信中,数据被以二进制形式发送,并且每次发送都只传输1比特位(当然,传输速度可能会很快),像SPI、IIC其实也属于串口。而对传输速度的描述,也就是波特率即是描述的每秒传输的比特位数量,例如波特率是9600,就代表该串口每秒可以传输9600bit的数据。与之相对应的并口,则是使用多个物理信道传输数据,一批数据会同时在多个物理信道上传输,这种并行通信会占用很多IO资源,但是传输速度也得到了显著提高。

UART作为一种异步通信方式,它不需要时钟线,在供电正常的情况下一共就只需要两根数据线,非常适合一对一的通信需求。CW32F030拥有3个UART外设,此文章介绍UART1的使用,那么按照惯例,先总览一下使用UART需要做哪些准备工作。

第一:需要将IO设置为输出,并复用为UART模式。

第二:需要使能UART这个外设,并设置通信协议的必要参数(关于UART协议本身,互联网有很多优质教程,读者可以自行搜索),完成初始化。

第三:编写发送和接收函数,调用函数实现发送和接收的功能。

第一步的进行非常简单,查询芯片《数据表》的引脚定义,发现LQFP48封装下,PA8 & PA9是UART1的发送和接收,因此只需要配置好这两个IO的功能,第一步就算是完成了。

1.png

2.png

配置代码如上图,但有读者表示自己找不到设置复用功能的函数,那我们就来看看功能复用函数的本质是什么。

3.png

这是PA9的复用函数,可以看到这是一个宏函数,其内容表明设置复用其实就是设置了对应GPIO复用寄存器的值,PA9属于A组IO的第9号口,是高位Pin口,因此要找到高位口的复用寄存器,并设置复用功能为(0010)2,也就是AF2,具体设置如下图所示。

4.png

因此配置复用的时候,如果找不到对应的复用函数,可以直接配置此寄存器来完成复用配置,但为了方便读者,配置IO复用的宏函数均位于cw32xxxxx_gpio.h这个文件中,此头文件包含了相当多的宏函数,均是常用的IO操作,如反转、置位、闪烁等,感兴趣的读者可以前往翻阅。

话题拽回到UART这边,接下来要进行的是第二步,配置UART的必要参数,这里先讲解发送功能需要进行的配置。

不论是什么单片机,想要使用任何一个片上外设,必须要使能该外设的时钟总线,直接在工程的函数列表中找到时钟配置文件下的全部函数,根据函数的名字可以快速定位使能UART1时钟的函数

我们可以看到这个函数本质上就是在操作APBEN2这个寄存器,通过查看芯片手册,我们也可以找到各个外设所属的时钟线。

5.png

6.png

随后就是UART通信参数的结构体了,结构体同样对应了寄存器,但使用结构体可以简化开发者的配置步骤,且看下图:

7.png

8.png

此结构体成员拥有相当详细的注释,这里直接按照开发者意图配置即可,我在这里配置为:波特率115200、PCLK时钟源、1个停止位、无校验位、全双工模式,配置完成后初始化结构体即可。

值得注意的是,在芯片内部并没有一个直接的波特率数值寄存器,为了保证异步通讯的可靠,波特率越精准越好,且既有的系统时钟不一定能够刚好提供波特率倍数的时基,因此实际上的波特率是一个浮点数,它拥有整数部分和小数部分的2个寄存器。具体的计算公式在芯片手册的通用异步收发器章节有写,这里只列举初始化函数中的相关操作:

9.png

由于我选择的是16倍采样,初始化函数会执行上述代码,代码的计算过程复刻了手册中的计算过程(如下图),这里是先计算时钟源在16倍采样模式下能够直接取得的波特率结果temp,temp包含了整数部分和小数部分,整数部分直接赋值即可,小数部分则是将公式变型得到的。开发者在使用时不需要手写计算BRRI和BRRF的寄存器值,初始化函数会自动完成该计算。

10.png

皆大欢喜,到目前为止已经成功完成两步了,还差最后一步就可以使用UART的发送功能了,那就是自己写一个UART1的发送函数。我手里刚好就有一个自己写的发送函数,长这个样子:

 11.png

这段简易代码会发送这个字符串作为UART1的调试参数。

12.png

这段代码的主要逻辑在芯片手册里面也有详细描述:

13.png

Tips:在编写各类通信函数这里,我建议一定要加上至少两个功能。第一个就是发送的状态,成功还是失败,这很重要,出问题之后的排查工作基本就靠这个了;第二个就是超时跳出功能,有的时候可能硬件故障或者什么别的原因导致发送一直失败,如果死等就让程序全部死掉了,超时跳出可以保证其他大部分程序的正常运行,当然使用看门狗是一种更优解。

在轮询函数中调用调试函数即可实现间隔100ms发送一次的功能,效果如下:

14.png

 15.png

从时间上来看,100ms的定时发送还是很精确的,这种方式占用CPU的时间资源实现发送,虽然传统但是有效可控。

要点总结:

1、注意区分串口和并口的概念。

2、CW32的UART虽然有2个波特率寄存器,但是并不需要开发者手算寄存器参数。

来源:CW32生态社区

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

围观 8

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

BD网盘链接:

https://pan.baidu.com/s/110FD7JQyuP-aBmEngzbsig?pwd=vi1l 

提取码:vi1l

一、实验简介

1. RC522 刷卡模块是应用于13.56MHz 非接触式通信中高集成度读写卡系列芯片中的一员,是NXP 公司针对“三表”应用推出的一款低电压、低成本、体积小的非接触式读写卡芯片。非接触式IC卡又称射频卡,由IC芯片、感应天线组成,常被用于智能仪表和便携式手持设备中,如“三表”(水表、电表、燃气表)应用。其射频卡技术,将射频识别技术和IC卡技术结合,结束了无源(卡中无电源)和免接触这一难题,为电子器件领域带来突破。卡片在一定距离范围(通常为5—10cm)靠近读写器表面,通过无线电波的传递来完成数据的读写操作。

二、实验器材

本实验使用到了CW32-48F大学计划开发板、TTS语音播报模块、RC522刷卡模块、IC钥匙扣卡及Keil5开发环境。

1.png

CW32-48F大学计划板    

2.png

TTS语音播报模块

3.png

RC522刷卡模块

4.png

IC钥匙扣卡

5.png

实物图

【RC522刷卡模块与单片机接线】:

3.3V -- 3.3V

RST -- PB15

GND -- GND

IRQ 悬空

MISO -- PB14

MOSI -- PB13

SCK -- PB12

SDA -- PA6

(注:采用SPI通信方式)

【TTS语音播报模块与单片机接线】:

黑线 -- GND

红线 -- 3.3V

黄线 -- PA9

白线 -- PA10

三、原理简介

RC522模块(射频读写器)向IC卡发一组固定频率的电磁波,卡片内有一个LC串联谐振电路,其频率与读写器发射的频率相同,这样在电磁波激励下,LC谐振电路产生共振,从而使电容内有了电荷;在这个电荷的另一端,接有一个单向导通的电子泵,将电容内的电荷送到另一个电容内存储,当所积累的电荷达到2V时,此电容可作为电源为其它电路提供工作电压,将卡内数据发射出去或接受读写器的数据。
非接触性IC卡与读卡器之间通过无线电波来完成读写操作。二者之间的通讯频率为13.56MHZ。非接触性IC卡本身是无源卡,当读写器对卡进行读写操作时,读写器发出的信号由两部分叠加组成:一部分是电源信号,该信号由卡接收后,与本身的L/C产生一个瞬间能量来供给芯片工作。另一部分则是指令和数据信号,指挥芯片完成数据的读取、修改、储存等,并返回信号给读写器,完成一次读写操作。

四、核心代码

main.c:
#include "main.h"
#include "Uart.h"
#include "stdio.h"
#include "Delay.h"
#include "RC522.h"
#include "Buzzer.h"
#include "Lcd_Driver.h"
#include "LCD_calculate.h"

#define MONRY 100    //IC卡初始金额

uint8_t RecPcd[2]={0};  //存放IC卡类型
uint8_t uid[4]={0};     //存放IC唯一序列号
uint8_t read[16]={0};   //存放读出的数据
uint8_t write[16]={0};  //存放写入的数据
uint8_t DefaultKey[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};//密钥(出厂默认)

void Card_Refresh(void)     //刷新卡的余额,仅在第一次使用IC卡或者IC金额刷光后使用
{  
    write[0]=MONRY;   //将初始金额存进要写入IC卡的数组中  
    RC522_PcdWrite(0x01,write);//将数据写入块1
}

int main()
{  
    char str[16]="";   //存放显示在屏幕上的字符串  
    Lcd_Init();        //TFT屏幕初始化  
    UART3_Init();      //串口3初始化,通过串口进行语音播报  
    Buzzer_Init();     //初始化板载蜂鸣器  
    RC522_Init();       //初始化RC522刷卡模块  
    Gui_DrawFont_GBK16(0,0,BLUE,WHITE,"      BUS       ");  //显示界面  
    TFTShowString(2,0,"price: 1 RMB  ");  //单次刷卡金额  
    while(1)  
    {      
        TFTShowString(4,0,"Waiting card...");    
        if(RC522_PcdRequest(PICC_REQALL,RecPcd)==MI_OK) //寻卡,有卡就认    
        {        
            GPIO_WritePin(CW_GPIOB,GPIO_PIN_3,GPIO_Pin_SET);  //寻卡成功则蜂鸣器发出“滴”的一声      
            Delay_ms(100);      
            GPIO_WritePin(CW_GPIOB,GPIO_PIN_3,GPIO_Pin_RESET);      
            if(RC522_PcdAnticoll(uid)==MI_OK)//防冲突,应对同时有多张卡的情况,将被选中卡片的序列号保存到uid中      
            {          
                if(RC522_PcdSelect(uid)==MI_OK) //选定序列号为uid的卡片        
                {          
                    if(RC522_PcdAuthState(PICC_AUTHENT1A,0x01,DefaultKey,uid)==MI_OK)//验证该卡片第一扇区A密钥          
                    {      
                        //            Card_Refresh();  //刷新卡的余额                    
                        if(RC522_PcdRead(0x01,read)==MI_OK)     //1.读取块1(16字节)数据            
                        {              
                            write[0]=read[0]-1;                   //2.读出数据的第一字节减去(车)票价,存到写入数据的第一字节中              
                            if(RC522_PcdWrite(0x01,write)==MI_OK) //3.写入数据到块1              
                            {                
                                if(RC522_PcdRead(0x01,read)==MI_OK) //4.读出刷卡后块1的数据,进行播报和显示                
                                {                    
                                    printf("刷卡成功");//语音播报刷卡成功                  
                                    TFTShowString(4,0,"Brushing card...");                  
                                    sprintf(str,"  balance:%d",read[0]);                  
                                    TFTShowString(6,0,str);//显示卡中余额                  
                                }              
                            }            
                        }          
                    }        
                }      
            }    
        }    
        Delay_s(1); //刷卡间隔    
        TFTShowString(6,0,"                "); //覆盖余额显示  
    }
}

五、效果演示

6.png

等待刷卡

7.png

正在刷卡   

来源:CW32生态社区

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

围观 18

2023慕尼黑华南电子展(electronica South China)于10月30-11月1日在深圳国际会展中心(宝安新馆)圆满举办。

1698906095426228.jpg

CW32精彩亮相慕尼黑华南电子展

武汉芯源半导体在展位上为电子行业参观者展示了CW32众多产品应用,比如智能指纹锁、智能燃气表、LED调光板、可燃气体探测器、料位开关、温控器、无线麦克风、筋膜枪、血氧仪、以太网模块、无刷电机等多款MCU典型应用方案,吸引了众多用户驻足参观了解产品。

1698906110305925.png

1698906120359103.png

1698906131856708.png

5.png

1698906143299701.jpg

CW32荣获 最佳MCU芯片奖

2023年10月30日,由芯师爷主办、慕尼黑华南电子展协办、深圳市半导体行业协会支持的“第五届硬核芯生态大会暨2023汽车芯片技术创新与应用论坛”在深圳国际会展中心1号馆圆满落幕!

01、为客户提供负责任的产品,CW32在行动

武汉芯源半导体有限公司技术总监张亚凡在“第五届硬核芯生态大会暨2023汽车芯片技术创新与应用论坛”上发表主题演讲《为客户提供“负责任”的产品,CW32在行动》,从设计端、生产制造端和技术服务端三个环节展示了CW32系列MCU的核心优势,就CW32系列MCU的核心优势、技术性能、功能特点、产品布局等方面进行了详细讲解。

1698906169594635.jpg

02、荣获“2023年度最佳MCU芯片奖”

峰会当晚,2023年度硬核芯评选颁奖盛典隆重揭晓终极榜单。在本次评选中,武汉芯源半导体有限公司从165家企业、183款产品中脱颖而出一举斩获“2023年度最佳MCU芯片奖”。

1698906189217886.jpg

1698906197860010.jpg

03、获奖产品:通用高性能CW32F003系列

武汉芯源半导体CW32F003系列产品已全面实现 -40℃ ~105℃超宽温度范围和 1.65V~5.5V 超宽工作电压;12位高速ADC,可达到±1.0 LSB INL,11.3 ENOB(有效位数);HBM ESD 测试通过8KV,具备超强抗干扰能力,HBM ESD、MM ESD、CDM ESD、Latch up@105℃全面达到JEDEC最高等级;EFT测试通过:±4000V(Power)/±2000V(IO);具备稳定可靠的eFLASH制造,确保工业高可靠应用。

1698906207771462.png

来源:武汉芯源半导体

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

围观 10

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

BD网盘链接:

https://pan.baidu.com/s/187ePq84u2QjE1bsVPM6i3g?pwd=9jvv 

提取码:9jvv

一、实验简介

本实验是使用CW32单片机制作一个入门的遥控循迹小车。遥控采用蓝牙配合手机APP进行遥控。循迹使用一个5路的循迹模块。使用OLED模块进行显示当前小车状态。

二、实验器材

本实验使用到了CW32F030C8小蓝板、智能小车底板、电机驱动模块、OLED模块、DX-BT04-E蓝牙模块、Wch-Link下载调试器、Keil5开发环境。

1.jpg

二、接线图

【DX-BT04-E模块与单片机连线】:

GND<-->GND

5V<-->5V

TXD<-->PA3

RXD<-->PA2

2.png

【Wch-Link模块与单片机连线】:

GND<-->GND

RXD<-->PB8

TXD<-->PB9

3.png

【TB6612电机驱动模块与单片机连线】:

VM<-->VM

GND<-->GND

VCC<-->VCC

AO1<-->AO1

AO2<-->AO2

BO1<-->BO1

BO2<-->BO2

PWMA<-->PA15

AN2<-->PA11

AN1<-->PA12

STBY<-->3.3V

BN1<-->PB4

BN2<-->PB5

PWMB<-->PB3

4.png

【OLED模块与单片机连线】:

GND<-->GND

VDD<-->3.3V

SCK<-->PB6

SDA<-->PB7

5.png

【循迹模块与单片机连线】:

GND<-->GND

VDD<-->5V

S1<-->PA0

S2<-->PA1

S3<-->PA7

S4<-->PA8

S5<-->PC14

6.png

实验完整接线图:

1698804435841426.png

四、用到的外设

1.串口2:

串口2用于蓝牙模块,使手机和单片机通过蓝牙模块进行串口透传,从 而进行点对点通信。

2.高级定时器ATIM:

初始化高级定时器CH1B和CH2B进行PWM波输出,从而达到电机调速目的。

3.基本定时器BTIM1:

初始化基本定时器1,在基本定时器中断服务函数中对循迹模块进行扫描。将扫描的结果保存,以便在循迹模式中使用。

4.IIC1:

IIC1用于外挂OLED显示模块。

五、核心代码

// 基本定时器中断回调函数
// 在回调函数中对循迹模块进行扫描,将循迹模块的状态保存进sensor_data中
void BTIM1_IRQHandler(void)
{    
    if (BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV)) // 判断是否是通道1中断    
    {
        BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV); // 清除中断标志位
      
      char t = 0x00; // 设置零时变量保存灰度传感器的值
      
      // 保存灰度传感器的值        
      // 采用或运算,检测到黑线将相应的位设置为1        
      if (!GPIO_ReadPin(TRACKING_GPIOA, TRACKING_1))        
      {            
          t |= 0x08; // 00001000 左边数第一个        
      }        
      if (!GPIO_ReadPin(TRACKING_GPIOA, TRACKING_2))        
      {            
          t |= 0x04; // 00000100 左边数第二个        
      }        
      if (!GPIO_ReadPin(TRACKING_GPIOA, TRACKING_3))        
      {            
          t |= 0x02; // 00000010 左边数以三个        
      }        
      if (!GPIO_ReadPin(TRACKING_GPIOA, TRACKING_4))        
      {            
          t |= 0x01; // 00000001 左边数第4个        
      }        
      if (!GPIO_ReadPin(TRACKING_GPIOC, TRACKING_5))        
      {            
          t |= 0x10; // 00010000 左边数第5个        
      }        
      sensor_data = t; // 将灰度传感器的值赋值给sensor_data    
   }
}

// 串口1中断处理函数
void UART1_IRQHandler(void)
{    
    unsigned char TxRxBuffer;    
    if (USART_GetITStatus(CW_UART1, USART_IT_RC) != RESET)    
    {        
        USART_ClearITPendingBit(CW_UART1, USART_IT_RC); // 清除中断标志位        
        TxRxBuffer = USART_ReceiveData_8bit(CW_UART1);  // 将接收到的数据放入TxRxBuffer
      
      USART_RX_BUF[rxIndex] = TxRxBuffer; // 将接收到的数据放入缓冲区
      
      if (rxIndex < USART_REC_LEN - 1) // 做数据长度的限制,留一个字节用于结束字符或者溢出检测        
      {            
          // 接收到的字符包含 \n 或者 \r 结束接收            
          if (USART_RX_BUF[rxIndex - 1] == '\n' || USART_RX_BUF[rxIndex - 1] == '\r')            
          {                
              USART_RX_BUF[rxIndex] = '\0'; // 在最后一个字节加上空字符,表示字符串结束            
          }            
          else            
          {                
              rxIndex++;            
          }        
      }        
      rxIndex = 0; // 清除数据标志        
      flag = 1;    // 清除    
  }
}

来源:CW32生态社区

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

围观 7

例程资料链接如下:

BD网盘链接:

https://pan.baidu.com/s/1uA4Rn-DjOeddqIz0f0WuEw 

提取码:naap

一、简介 

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引脚  w
    hile(1)  
    {    
        Key_Scan();     //扫描按键并执行相应功能    
        LED_Indicator();//指示灯  
    }
}

四、效果演示+说明

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

 6.png

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

7.png

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

8.png

来源:CW32生态社区

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

围观 24

页面

订阅 RSS - CW32