CW32

一、简介

1.这期视频向大家介绍如何使用CW32单片机通过ESP8266连接OneNET物联网平台,发送温湿度数据到平台,并接受平台的云控制。

二、所需物料

2.本实验使用到了CW32-48F大学计划开发板、DHT11温湿度传感器模块、ESP8266WIFI模块及Keil5开发环境。

1.png

CW32-48F大学计划板

2.png

DHT11温湿度传感器模块

3.png

ESP8266WIFI模块

 4.png

ESP8266WIFI模块引脚分布

5.png

实物图

3.接线:根据原理图,把模块连接到开发板上

(1)ESP8266WIFI模块:

开发板上预留了该模块接口,通过串口向ESP8266写入指令。

6.png

(2)DHT11模块:开发板上预留了该模块接口

7.png

4.(1)MQTT协议介绍:

MQTT是一种轻量级的发布/订阅消息传输协议,常用于物联网领域。它的特点是简化和减少了网络流量,可以高效地进行数据传输。在本次应用中,我们将使用MQTT协议进行数据上传。

(2)OneNET代码介绍:

在使用OneNet平台时,我们需要使用TCP协议进行连接。首先需要设置好TCP协议的相关参数,然后通过POST方法将温湿度数据上传到OneNet平台中。在上传数据时需要使用OneNet平台提供的API接口进行数据传输。

注:#define  PROID "XXX"  //xxx为产品ID

#define ACCESS_KEY      "XXX"   //xxx为acess_key访问密钥

#define DEVICE_NAME    "XXX"  //xxx为设备名称

8.png

9.png

三、核心代码

main.c

int main(void)
{    
    unsigned short timeCount = 0;  //发送间隔变量    
    unsigned char *dataPtr = NULL;   
    
    Hardware_Init();        //初始化外围硬件   
    while(DHT11_Init())    //检测DHT11    
    Delay_ms(200);  
    ESP8266_Init();          //初始化ESP8266  
    UsartPrintf(USART_DEBUG, "Connect MQTTs Server...\r\n");  
    while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT")) //ESP8266联网    
    Delay_ms(500);  
    UsartPrintf(USART_DEBUG, "NET_OK\r\n");    
    while(OneNet_DevLink())      //接入OneNET    
    Delay_ms(500);  
    OneNET_Subscribe();     //订阅消息传输,允许接受下发指令  
    TFTShowString(0,0,"     OneNET     ");  //界面  
    TFTShowString(1,0,"  智能物联平台  ");  
    TFTShowString(3,0,"humidity:    %");  
    TFTShowString(4,0,"temperature:  C");  
    while(1)  
    {    
        if(++timeCount >= 50)                      
        {      
            DHT11_Read_Data(&temperature,&humidity);  //读取温湿度值        
            TFTShowNumber(3,10,humidity);      
            TFTShowNumber(4,12,temperature);      
            UsartPrintf(USART_DEBUG,"TEMP:%d HUMI:%d\r\n",temperature,humidity);      
            UsartPrintf(USART_DEBUG, "OneNet_SendData\r\n");      
            OneNet_SendData();        //数据上传      
            timeCount = 0;      
            ESP8266_Clear();    //清除缓存    
        }       
        
        dataPtr = ESP8266_GetIPD(0); //接收OneNET平台返回的数据    
        if(dataPtr != NULL)      
        OneNet_RevPro(dataPtr); //确认返回数据,执行下发命令     
        
        Delay_ms(10);  
    }
}

Onenet.c:

#define PROID      "3OB4R7cKCw"       //产品ID(名称)
#define ACCESS_KEY    "n7GptYNpf9U97zHTz2ltc1Pvy4GB+0MXsU1nKel9ixc="  //产品访问密钥
#define DEVICE_NAME    "111"     //设备ID(名称)
void OneNet_RevPro(unsigned char *cmd)
{  
    char *req_payload = NULL;  
    char *cmdid_topic = NULL;  
    unsigned short topic_len = 0;  
    unsigned short req_len = 0;  
    unsigned char qos = 0;  
    static unsigned short pkt_id = 0;  
    unsigned char type = 0;  
    short result = 0;  
    char *dataPtr = NULL;  
    char numBuf[10];  
    int num = 0;   
    
    type = MQTT_UnPacketRecv(cmd);  
    switch(type)  
    {    
        case MQTT_PKT_PUBLISH:                                //接收的Publish消息       
        result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);      
        if(result == 0)      
        {        
            char *data_ptr = NULL;        
            UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",  cmdid_topic, topic_len, req_payload, req_len);        
            data_ptr = strstr(cmdid_topic, "request/");                  //查找cmdid        
            if(data_ptr)        
            {          
                char topic_buf[80], cmdid[40];    
                       
                data_ptr = strchr(data_ptr, '/');          
                data_ptr++;           
                
                memcpy(cmdid, data_ptr, 36);                      //复制cmdid          
                cmdid[36] = 0;          
                snprintf(topic_buf, sizeof(topic_buf), "$sys/%s/%s/cmd/response/%s",                              PROID, DEVICE_NAME, cmdid);          
                OneNET_Publish(topic_buf, "ojbk");                    //回复命令        
            }      
        }      
        case MQTT_PKT_PUBACK:                            //发送Publish消息,平台回复的Ack          
        if(MQTT_UnPacketPublishAck(cmd) == 0)        
        UsartPrintf(USART_DEBUG, "Tips:  MQTT Publish Send OK\r\n");    
        break;    
        case MQTT_PKT_SUBACK:   
                                     //发送Subscribe消息的Ack       
        if(MQTT_UnPacketSubscribe(cmd) == 0)        
            UsartPrintf(USART_DEBUG, "Tips:  MQTT Subscribe OK\r\n");      
        else        
            UsartPrintf(USART_DEBUG, "Tips:  MQTT Subscribe Err\r\n");    
        break;    
        default:      
        result = -1;    
        break;  
    }   
    
    ESP8266_Clear();                  //清空缓存   
    
    if(result == -1)    
    return;   
    dataPtr = strchr(req_payload, ':');          //搜索':'  
    if(dataPtr != NULL)          //如果找到了  
    {    
        dataPtr++;    
        while(*dataPtr >= '0' && *dataPtr <= '9')    //判断是否是下发的命令控制数据    
        {      
            numBuf[num++] = *dataPtr++;    
        }    
        numBuf[num] = 0;    
        num = atoi((const char *)numBuf);        //转为数值形式    
        if(strstr((char *)req_payload, "light"))    //搜索"light",判断":"后面的数值进行控制    
        {      
            if(num == 1)                //控制数据如果为1,代表开      
            {        
                UsartPrintf(USART_DEBUG, "接收到1\r\n");        
                //开灯        
                PA07_SETLOW();        
                TFTShowString(5,0,"light: ON ");      
            }      
            else if(num == 0)              //控制数据如果为0,代表关      
            {        
                UsartPrintf(USART_DEBUG, "接收到0\r\n");        
                //关灯        
                PA07_SETHIGH();        
                TFTShowString(5,0,"light: OFF");      
            }    
        }  
    }  
    if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH)  
    {    
        MQTT_FreeBuffer(cmdid_topic);    
        MQTT_FreeBuffer(req_payload);  
    }
}

来源:CW32生态社区

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

围观 11

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

BD网盘链接:

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

提取码:sfrv

一、简介

1.MAX4466是一款低噪声、高增益的放大器模块。该模块采用了集成放大器芯片,能够在低电压下提供高增益。它的输入阻抗较高,能够与多种信号源(例如麦克风、传感器)实现良好的匹配。同时,MAX4466还具备很低的噪声水平,能够有效地提取和放大微弱的信号,提高系统的灵敏度。MAX4466模块的应用场景包括但不限于以下几个方面:

音频处理:可以作为音频信号的前置放大器,将低电平的音频信号放大到适合后续处理的水平,如音乐播放、语音识别、语音通信等。声音检测与分析:结合麦克风等音频传感器,可以实现声音的检测、分析和识别,如声音传感器、语音识别设备等。无线通信:可以用于接收无线通信模块的输入信号,对其进行放大,以提高通信距离和信号质量。

二、所需物料

本实验使用到了CW32f030c8小蓝板、MAX4466模块、LED交通信号灯模块、0.96寸OLED显示屏、Keil5开发环境及VOFA+上位机软件。

1.png

CW32F030C8小蓝板

2.png

MAX4466模块

3.png

实物(俯视)

4.png

实物(正视)

【单片机与MAX4466连线】:3.3V---VCC

GND---GND

PA0---OUT

【单片机与OLED屏连线】: GND---GND  

3.3V--VDD  

PB8--SCK  

PB9--SDA

【单片机与LED灯连线】:  GND--GND

PC15--R  

PC14--Y  

PC13--G

三、核心代码

ADC.c:
#include "ADC.h"
#include "main.h"
#include "delay.h"

void ADC_Configuration(void) 
{   
    ADC_SingleChTypeDef ADC_SingleInitStruct; 
     
    __RCC_ADC_CLK_ENABLE();    // ADC时钟使能  
    __RCC_GPIOA_CLK_ENABLE();  //GPIOA使能  
    PA00_ANALOG_ENABLE();  
    ADC_SingleInitStruct.ADC_Chmux = ADC_ExInputCH0;                //PA00 通道0  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_AccEn = ADC_AccDisable;   //转换结果累加不使能  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_Align = ADC_AlignRight;   //ADC转换结果右对齐  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_ClkDiv = ADC_Clk_Div16;   //PCLK    
    ADC_SingleInitStruct.ADC_InitStruct.ADC_DMAEn = ADC_DmaDisable;   //关闭DMA传输  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_InBufEn = ADC_BufEnable;  //开启跟随器  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_OpMode = ADC_SingleChOneMode;//单通道单次转换模式     
    ADC_SingleInitStruct.ADC_InitStruct.ADC_SampleTime = ADC_SampTime10Clk; //10个ADC时钟周期  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_TsEn = ADC_TsDisable;    //内置温度传感器禁用  
    ADC_SingleInitStruct.ADC_InitStruct.ADC_VrefSel = ADC_Vref_VDDA; //VDDA参考电压 
   
   ADC_SingleChOneModeCfg(&ADC_SingleInitStruct);                  //开始转换  
   ADC_Enable();                                                   // 使能ADC   
   
   ADC_SoftwareStartConvCmd(ENABLE);                                //转换使能
}

uint16_t Get_Data(void) //ADC数据采集
{  
    unsigned int temp_dat_adc = 0;   
    
    ADC_SoftwareStartConvCmd(ENABLE);       //开始转换  
    while(ADC_GetITStatus(ADC_IT_EOC))  
    {    
        ADC_ClearITPendingBit(ADC_IT_EOC);    //清除标志位        
        temp_dat_adc=ADC_GetConversionValue();//电压采集      
    }  
    return   temp_dat_adc;}

uint16_t Cal_Average_Data(void)
{  
    float dat;  
    uint8_t i=0;  
    for(i=0;i<50;i++)   //连续采集50次ADC的值  
    {    
        dat+=Get_Data();  //累加    
        Delay_ms(1);  
    }  
    return dat/50;      //返回平均值
}
main.c:
int main()  
{  
    uint16_t temp;  
    LED_Init();  //LED灯初始化  
    OLED_Init(); //OELD屏初始化  
    UART_Init(); //串口初始化  
    ADC_Configuration(); //ADC配置  
    OLED_ShowString(1,1,"present:");   //提示字符串  
    OLED_ShowString(2,1,"record_max:");  
    while(1)  
    {    
        temp=Cal_Average_Data();  //获取ADC数值累加后的平均值    
        printf("%d\n",temp);   //通过串口借助VOFA+软件打印波形图    
        LED_Indicator(temp);    //LED灯指示    
        Delay_ms(100);     
    }
}

四、VOFA+软件简易使用说明

5.png

6.png

7.png

①配置协议与连接:数据引擎选择”FireWater”,(注意:在程序中使用printf函数时,必须在%d后面加上\n才能在FireWater模式下被上位机正确识别),数据接口选择”串口”,正确选择端口号和配置波特率;

②在控件中找到波形图,拖拽到主窗口中,选择整窗口填充;

8.png

9.png

④右击窗口选择Y轴中的All打开Y轴数据显示;

⑤在图形种类中(默认)选择波形图;

10.png

⑥点击左上角灰黑色圆圈打开串口进行通信,看到圆圈变蓝并且闪烁代表通信正常,之后可以像调节示波器显示一样通过鼠标滚轮手动将波形调整到合适的显示范围,也可以点击Auto一键调整波形显示范围。

软件官网下载地址:https://www.vofa.plus/downloads/

五、效果演示

11.png

正常音量   12.png

正常音量波形图

13.png   

较大音量

14.png 较大音量波形图

15.png

过大音量
16.png

过大音量波形图

来源:CW32生态社区

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

围观 15

相关文章:

【CW32学习笔记】单片机启动&库函数构成

【CW32学习笔记】看手册配置时钟树

我们在前两节讲解了单片机启动到时钟树配置的内容,到此为止,单片机已经能开发别的功能并使用了,但这里我插入一个笔者个人觉得很重要的章节来介绍一个内核外设——滴答定时器(System Tick)。

根据cortex-M0+内核手册的介绍,这是一个24位倒计时定时器,它拥有4个可以访问的寄存器,

1.png

这个定时器和一般的定时器用法没什么区别,他没有高级定时器的功能,但是由于是内核白送的定时器,所以经常用于单片机的心跳时钟。

值得注意的是,滴答定时器是一个不可修改计数方式的定时器,他只能向下计数,查阅寄存器定义之后,进行如下代码的初始化操作,即可实现1ms定时并开启定时器中断。

2.png

开启定时器并打开中断之后,定时器就能正常工作,并且会正常进入其对应的中断服务函数,中断服务函数的名字可以直接在启动文件中找到(还记得第一节讲的中断向量表吗?)。

3.png

一般情况下,芯片厂商不会主动提供滴答定时器的中断服务函数接口,也就是xxxxx_it.c文件中不会出现此中断服务函数的名字(CW32的库会提供一个参考stm32 hal库的固件库,但是我觉得写的不符合我的风格,索性自己写了一个滴答定时器的文件),任何时候,只需要有这么个同名函数存在于工程文件中,该中断服务函数就能被正常调用并执行,例如这样:

4.png

实际上,任何中断服务函数都可以用这种办法来重写一个自己的,只是大部分时候官方都已经提供了良好的固件库,也就不需要我们手动重写了。

这里编写了一个测试函数来测试滴答定时器是否正常工作,实际上是使用延时函数来验证的:

5.png

那么结果当然是正常工作的!

在这里给一些建议,上手一款新的芯片的时候,完全可以直接用滴答定时器点灯来测试和上手。

总结:

1、滴答定时器是内核外设,芯片手册不会记录它的用法,内核手册才会有它的用法。

2、滴答定时器可以作为整个程序的心跳时钟,非常常用。

来源:CW32生态社区

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

围观 17

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

BD网盘链接:

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

提取码:oqe6

一、简介

1. MPU6050是一种常用的六轴姿态传感器模块,结合了三轴陀螺仪和三轴加速度计,以及一个可扩展的数字运动处理器DMP(Digital Motion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。MPU6050 对陀螺仪和加速度计分别用了三个16 位的ADC(0~65535),将其测量的模拟量转化为可输出的数字量。为了精确跟踪快速和慢速的运动,传感器的测量范围都是用户可控的,陀螺仪可测范围为±250,±500,±1000,±2000°/秒(dps),加速度计可测范围为±2,±4,±8,±16g。并有可编程的低通滤波器。MPU6050模块的应用场景非常广泛,包括但不限于以下几个领域:

(1)姿态感知与控制:通过读取陀螺仪和加速度计的数据,可以实时获取物体的姿态信息,如倾斜角度、旋转角度等。这在飞行器、机器人、无人车等项目中非常常见,用于实现姿态感知和控制。

(2)运动跟踪与手势识别:MPU6050可以用于捕捉人体运动轨迹,如手部的姿态和手势,从而实现运动跟踪、手势识别、虚拟现实交互等应用。

(3)震动检测与防抖:结合加速度计,MPU6050可以检测到物体的震动和冲击,用于防抖技术、硬盘保护、运动检测等。

(4)步态分析与健康监测:MPU6050可以用于分析人体的步态特征和动作,用于健康监测、运动分析、姿势校正等。

二、所需物料+寄存器说明

.本实验使用到了CW32F030C8小蓝板、GY-521模块、0.96寸OLED显示屏Keil5开发环境

1.png

CW32F030C8小蓝板

2.png   

GY-521模块

   3.png

实物展示

【GY-521模块与单片机连线】:VCC<-->+3.3V

GND<-->GND

SCL<-->PB5

SDA<-->PB4

【OLED显示屏与单片机连线】:VCC<-->+3.3V

GND<-->GND

SCL<-->PA1

SDA<-->PA2

注:SCL和 SDA是连接MCU的 IIC接口,MCU通过这个IIC 接口来控制MPU6050,另外还有一个 IIC 接口: XCL和XDA ,这个接口可用来连外部从设备比如磁力计,这样就可以组成一个九轴传感器。AD0是MPU6050的地址控制引脚,该引脚控制的是IIC 地址的最低位。MPU6050的默认IIC地址是:0X68,如果AD0接VDD,则是0X69。需要注意的是:这里的地址0x68(110 1000)和0x69(110 1001)是不包含最低位的7位数据,通常最低位用于表示IIC主机的读取数据/写数据模式。如默认情况下对MPU6050进行写操作,则发送地址0xD0(1101 0000),读操作则发送地址0xD1(1101 0001)。

寄存器说明:

 4.png

该寄存器是配置陀螺仪输出速率的分频器,用于为MPU-6050生成采样速率。这里有个公式:采样频率=陀螺仪输出频率/(1+采样分频数)。当 DLPF(数字低通滤波器,见寄存器Configuration)禁用时,陀螺仪输出频率为8kHz;当 DLPF 使能,陀螺仪输出频率=1KHz。 5.png

该寄存器为陀螺仪和加速度计配置外部帧同步(FSYNC) 管脚的采样和数字低通滤波(DLPF)设置。其中,数字低通滤波器DLPF由DLPF_CFG配置。根据下表所示的DLPF_CFG值对加速度计和陀螺仪进行滤波。

 6.png

FS为陀螺仪输出频率。SMPLRT_DIV由预设定的采样频率根据上述的公式计算得出。一般情况下,DPLF滤波频率为采样频率的一半,如设定采样频率为50Hz,由表可知当FS为1kHz,SMPLRT_DIV的值为1000/50-1=19。

6.png 

该寄存器是用来触发陀螺仪自检和配置陀螺仪的满量程范围。其中,XG_ST、YG_ST、ZG_ST分别用来设置陀螺仪X轴、Y轴、Z轴自检,置0则不触发自检。FS_SEL[1:0]用于设置陀螺仪的满量程,如下表:

 7.png

我们一般设置为3,即满量程为±2000°/s

8.png

该寄存器是用来触发加速度计自检和配置加速度计的满量程范围。同时这个寄存器也可以用于配置数字高通滤波器(DHPF)。其中,XA_ST、YA_ST、ZA_ST分别用来设置加速度计X轴、Y轴、Z轴自检,置0则不触发自检。AFS_SEL[1:0]用于选择加速度计的满量程范围,如下表:

 9.png

我们一般设置为0,即满量程为±2g

 10.png

ACCEL_XOUT :由 2部分组成的 16位数值存储最近X 轴加速度计的测量值。ACCEL_YOUT :由 2部分组成的 16位数值存储最近Y 轴加速度计的测量值。ACCEL_ZOUT :由 2部分组成的 16位数值存储最近Z 轴加速度计的测量值。

以ACCEL_XOUT为例,若倍率设定为2g,则意味着ACC_X取最小值-32768时,当前加速度为沿X轴正方向2倍的重力加速度;若设定为4g,取-32768时表示沿X轴正方向4倍的重力加速度,以此类推。显然,倍率越低精度越好,倍率越高表示的范围越大,这要根据具体的应用来设定。以ACC_X为例,若当前设定的加速度倍率为4g,那么将ACC_X读数换算为加速度的公式为:

11.png

g可取当地重力加速度。

12.png

该寄存器存储最近加陀螺仪的测量值,构成与加速度计测量值寄存器相同,不做赘述。

以GYR_X为例,若倍率设定为250度/秒,则意味着GYR取正最大值32768时,当前角速度为顺时针250度/秒;若设定为500度/秒,取32768时表示当前角速度为顺时针500度/秒。显然,倍率越低精度越好,倍率越高表示的范围越大。以GYR_X为例,若当前设定的角速度倍率为1000度/秒,那么将GRY_X读数换算为角速度(顺时针)的公式为:

 13.png

14.png

该寄存器允许用户配置电源模式和时钟源,还提供了复位整个设备和禁用温度传感器的位。当置SLEEP位为1时,MPU-60X0 可以进入低功耗睡眠模式。该寄存器的最低三位用于设置系统的时钟源选择,默认值是0(内部8M RC振荡),不过一般设置为1,即选择x轴陀螺仪PLL作为时钟源,以获得更高精度的时钟。DEVICE_RESET该位置 1,重启内部寄存器到默认值。复位完成后该位自动清0。TEMP_DIS该位置 1,禁用温度传感器。

三、核心代码

main.c:
#include "main.h"
#include "OLED.h"
#include "GY_521.h"
#include "MYI2C.h"
#include "Delay.h"    
typedef struct
{  
    int16_t AX;  
    int16_t AY;  
    int16_t AZ;  
}MPU6050_Adata;  //MPU6050加速度计三轴数据

typedef struct
{  
    int16_t GX;  
    int16_t GY;  
    int16_t GZ;  
}MPU6050_Gdata;  //MPU6050陀螺仪三轴数据

MPU6050_Adata Adata;  //结构体变量
MPU6050_Gdata Gdata;

void GY_521_Init(void)  //GY-521初始化
{  
    GY521_GPIO_Init();  //GPIO初始化  
    //解除睡眠,失能温度传感器,选择X轴的陀螺仪时钟  
    WriteData(GY521_ADDR, MPU6050_PWR_MGMT_1, 0x09);   
    WriteData(GY521_ADDR, MPU6050_CONFIG, 0x06); //低通滤波  
    WriteData(GY521_ADDR, MPU6050_SMPRT_DIV, 0x09); //1KHz十分频为100Hz  
    WriteData(GY521_ADDR, MPU6050_GYRO_CONFIG, 0x18);//陀螺仪最大量程  
    WriteData(GY521_ADDR, MPU6050_ACCEL_CONFIG, 0x18);//加速度计最大量程
}

void MPU6050_GetData()  //获取MPU6050六轴数据
{  
    uint8_t MPU6050_Raw_Data[14]={0};  
    //以MPU6050_ACCEL_XOUT_H为起始地址,连续读取14字节的数据  
    ReadData(GY521_ADDR,MPU6050_ACCEL_XOUT_H,MPU6050_Raw_Data,14);  
    //数据处理      
    Adata.AX=(MPU6050_Raw_Data[0]<<8)|MPU6050_Raw_Data[1];  
    Adata.AY=(MPU6050_Raw_Data[2]<<8)|MPU6050_Raw_Data[3];  
    Adata.AZ=(MPU6050_Raw_Data[4]<<8)|MPU6050_Raw_Data[5];  
    Gdata.GX=(MPU6050_Raw_Data[8]<<8)|MPU6050_Raw_Data[9];  
    Gdata.GY=(MPU6050_Raw_Data[10]<<8)|MPU6050_Raw_Data[11];  
    Gdata.GZ=(MPU6050_Raw_Data[12]<<8)|MPU6050_Raw_Data[13];
}

int main()
{  
    OLED_Init();   //OLED初始化  
    GY_521_Init(); //GY-521初始化  
    OLED_ShowString(1,1,"A:      G:");//提示:左列显示加速度计数据;右列显示陀螺仪数据  
    while(1)  
    {    
        MPU6050_GetData(); //获取六轴数据    
        OLED_ShowSignedNum(2,1,Adata.AX,5);    
        OLED_ShowSignedNum(3,1,Adata.AY,5);    
        OLED_ShowSignedNum(4,1,Adata.AZ,5);     
        
        OLED_ShowSignedNum(2,9,Gdata.GX,5);    
        OLED_ShowSignedNum(3,9,Gdata.GY,5);    
        OLED_ShowSignedNum(4,9,Gdata.GZ,5);    
        Delay_ms(100); //延时刷新  
    }
}

四、效果演示

15.png

平放

 16.png

向下倾斜

 17.png 

左倾斜向

18.png 

直立

读到的原始数据还不能直接使用,要转化成四元数,欧拉角后,获得器件的姿态角才有用,而 MPU6050 自带了数字运动处理器,即 DMP,并且,InvenSense 提供了一个 MPU6050 的嵌入式运动驱动库,结合 MPU6050 的 DMP,可以将我们的原始数据,直接转换成四元数输出,而得到四元数之后,就可以很方便的计算出欧拉角,从而得到yaw、roll和pitch。

来源:CW32生态社区

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

围观 30

例程资料链接如下:

BD网盘链接:

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

提取码:855c

一、简介

1.GY-33是一种基于TCS34725颜色传感器的颜色识别模块。TCS34725是一种高精度光学传感器,能够检测红、绿、蓝三个基本色的光谱信息,从而实现对物体颜色的准确识别,该模块的具体应用场景包括以下几个方面:

(1)电子设备颜色校准:在电子设备制造过程中,颜色一致性非常重要。使用GY-33模块可以帮助制造商检测和校准电子设备的颜色,确保不同设备之间的颜色表现一致。

(2)色彩分析和精确匹配:GY-33模块在颜色分析和匹配方面有广泛应用。例如,在印刷行业中,可以使用该模块来检测和匹配颜色样本,从而确保印刷品的准确颜色表现。

(3)机器人视觉系统:GY-33模块可以用于机器人视觉系统,帮助机器人在环境中对不同颜色的物体进行识别和分类。这在物流、仓储和自动导航系统中非常有用,机器人可以根据物体的颜色属性执行相应的任务。

二、所需物料

本实验使用到了CW32F030C8小蓝板、GY-33颜色识别模块、0.96寸OLED显示屏,RGB全彩LED模块、轻触开关模块及Keil5开发环境。

1.jpg

CW32F030C8小蓝板

2.png

GY-33颜色识别模块

3.png   

RGB全彩LED模块

4.png

实物展示

【GY-33与单片机连线】:VCC<-->+3.3V

   GND<-->GND

   DR<-->PA5

   CT<-->PA4

【LED与单片机连线】:V<-->+3.3V

        R<-->PA0

    G<-->PA1

    B<-->PA2

【轻触开关与单片机连线】:VCC<-->+3.3V

GND<-->GND

OUT<-->PB9

此模块有两种方式读取数据,即串口UART或者 MCU_IIC,本次实验采用MCU_IIC的方式。

5.png

6.png

7.png

有简单的7种颜色识别,单片机不参与数据处理工作,不需要计算RGB值,直接读取吧 数据即可。需要特别注意的是:

8.png

三、核心代码

main.c:
#include "main.h"
#include "RGB.h"
#include "GTIM.h"
#include "Delay.h"
#include "GY_33.h"
#include "OLED.h"
#include "Key.h"
#include "BTIM.h"

#define LENGTH 3   //读取数据的长度
uint8_t press_flag=0; //按键按下标识
uint8_t data[LENGTH]={0}; //存放读取到的RGB数据
uint8_t color[1]={0};  //存放模块识别到的颜色数据
char *str[]={"blue","dblue","green","black","white","pink","yellow","red"};//模块可以识别到的颜色

int main()
{    
    uint8_t i;    
    OLED_Init();      //OLED显示  
    RGB_GPIO_Init();  //RGB灯GPIO初始化  
    GTIM2_Init();     //GTIM2初始化配置为PWM输出模式  
    I2C_GPIO_Init();  //GY-33模块GPIO初始化  
    Key_GPIO_Init();  //按键GPIO初始化  
    BTIM_Init();      //BTIM定时器初始化,定时控制按键扫描周期
    //  WriteData(GY33_ADDR,Config,0x51);  //启动白平衡,等级亮度为5
  
  while(1)  
  {    
      if(press_flag==1)  //若按键标识已打开,代表有按键按下,执行按键功能    
      {      
          OLED_Clear(); //清屏      
          if(ReadData(GY33_ADDR,R,data,LENGTH)) //读取模块检测颜色并进行处理后返回的RGB值      
          {        
              OLED_ShowString(1,1,"RGB:");        
              OLED_ShowNum(1,5,data[0],3);  //R值        
              OLED_ShowNum(2,5,data[1],3);  //G值        
              OLED_ShowNum(3,5,data[2],3);  //B值      
          }      
          RGB_Running(data);  //RGB全彩LED灯根据读取到的RGB进行显示      
          Delay_ms(100);      //数据读取间隔应不小于100ms      
          if(ReadData(GY33_ADDR,Color,color,1))//读取模块检测颜色并进行处理后的颜色信息返回值      
          {        
              for(i=0;i<8;i++) //8-bits数据,逐位判断        
              {          
                  if((color[0]>>i)==1) //判断哪一位为1          
                  {            
                      OLED_ShowString(4,1,"Color:");            
                      OLED_ShowString(4,7,str[7-i]); //显示对应颜色            
                      break;          
                  }        
              }      
          }      
          press_flag=0; //执行完关闭按键标识    
      }    
  }
}

void BTIM1_IRQHandler(void)  //BTIM1中断服务函数
{  
    static unsigned int cnt = 0;   
    
    if(BTIM_GetITStatus(CW_BTIM1,BTIM_IT_OV))    
    {    
        if(++cnt>=20)  //20ms定时,执行一次按键扫描    
        {      
            cnt = 0;      
            if(Key_Scan()==1)  //返回值不为0时        
            press_flag=1;    //打开按键标识    
        }    
        BTIM_ClearITPendingBit(CW_BTIM1,BTIM_IT_OV); //清除标志位  
    }
}

GY-33.c:
#include "main.h"
#include "Delay.h"
#include "GY_33.h"

void I2C_GPIO_Init(void)   //GY-33颜色识别模块GPIO初始化
{  
    __RCC_GPIOA_CLK_ENABLE();   
    
    GPIO_InitTypeDef GPIO_InitStruct;   
    GPIO_InitStruct.IT=GPIO_IT_NONE;  
    GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_OD;      //开漏输出  
    GPIO_InitStruct.Pins=GPIO_PIN_4|GPIO_PIN_5;  
    GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;  
    GPIO_Init(CW_GPIOA, &GPIO_InitStruct);  
     
    SCL(1);  
    SDA(1);
}

void I2C_Delay() //I2C延时函数
{  
    Delay_us(time);
}

uint8_t I2C_Start(void)  //发送起始信号
{  
    SDA(1);  
    SCL(1);  
    I2C_Delay();  
    if(ReadSDA==0) return 0;  
    SDA(0);  
    I2C_Delay();  
    if(ReadSDA==1) return 0;  
    SCL(0);  
    I2C_Delay();   
    
    return 1;
}

void I2C_Stop(void)  //发送停止信号
{  
    SDA(0);  
    SCL(0);  
    I2C_Delay();  
    SCL(1);  
    I2C_Delay();  
    SDA(1);
}

void I2C_SendACK(uint8_t ackbit)  //发送应答
{  
    SDA(ackbit);  
    SCL(1);  
    I2C_Delay();  
    SCL(0);  
    I2C_Delay();
}

void I2C_SendByte(uint8_t Byte)  //发送1字节(8-bit)的数据
{  
    uint8_t i;  
    SCL(0);  
    for (i = 0; i < 8; i++)  
    {    
        if(Byte&0x80) SDA(1);    
        else SDA(0);    
        SCL(1);    
        I2C_Delay();    
        SCL(0);    
        Byte<<=1;    
        I2C_Delay();  
    }
}

uint8_t I2C_ReceiveByte(void)  //接收1字节(8-bit)的数据
{  
    uint8_t data,i;  
    SDA(1);  
    Delay_us(1);  
    for(i=0;i<8;i++)  
    {    
        SCL(1);    
        data<<=1;    
        if(ReadSDA==1) data|=0x01;    
        I2C_Delay();    
        SCL(0);    
        I2C_Delay();  
    }   
    
    return data;
}

uint8_t I2C_WaitAck(void) //等待应答
{  
    uint16_t i;  
    SDA(1);  
    SCL(1);  
    while(ReadSDA==1)  
    {    
        if(++i==500)      
        break;  
    }  
    if(ReadSDA==1)  
    {    
        SCL(0);    
        return 0;  
    }  
    I2C_Delay();  
    SCL(0);  
    I2C_Delay();   
    return 1; 
}

uint8_t WriteData(uint8_t Slave_Addr,uint8_t REG_Addr,uint8_t data)  //写操作
{  
    if(I2C_Start()==0) RETURN   
    
    I2C_SendByte(Slave_Addr);    
    if(I2C_WaitAck()==0) RETURN  
     
    I2C_SendByte(REG_Addr);          
    if(I2C_WaitAck()==0) RETURN                   
    
    I2C_SendByte(data);  
    if(I2C_WaitAck()==0) RETURN                 
    
    I2C_Stop();              //发送停止信号   
    
    return 1;
}

uint8_t ReadData(uint8_t Slave_Addr,uint8_t REG_Addr,uint8_t *data,uint8_t length) //读操作
{    
    if(I2C_Start()==0) RETURN  
             
    I2C_SendByte(Slave_Addr);      
    if(I2C_WaitAck()==0) RETURN           
    
    I2C_SendByte(REG_Addr);              
    if(I2C_WaitAck()==0) RETURN   
    
    if(I2C_Start()==0) RETURN  
     
    I2C_SendByte(Slave_Addr+1);   
    if(I2C_WaitAck()==0) RETURN                   
    
    while(--length)
    {    
        *data++=I2C_ReceiveByte();    
        I2C_SendACK(0);    
        Delay_ms(110);  
    }  
    *data=I2C_ReceiveByte();  
    I2C_SendACK(1);  
    I2C_Stop();  
                      //发送停止信号   
    return 1;              
}

四、实物展示+效果演示

10.png

11.png

12.png

13.png

来源: CW32生态社区

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

围观 60

一、选择目标控制设备

在我们有着理论基础的情况下去开发一款水泵,我们就需要在市面上看一看已有的产品。

1.png

水泵的类型还是很多的,我们也是在网上淘了一款进行拆卸,只需要水泵,不需要它的控制器。我们需要把UVW三个相线引出来。

2.png

3.png

4.png

可以看见这是我们刚拆开水泵,取开壳子的照片,左下角是水泵的控制器,加了很大一块散热片,由此可见,产品功率还是很高的。在我们后面调试的时候,水泵全功率运行是400-500W左右。设备跑了15分钟左右,一桶水的温度都上升了几度,可见热量还是很多,所以对于大功率的设备,散热很重要。

二、芯片选型

在开发这个水泵控制器之前我们肯定要进行芯片选型、画板子以及写程序等工作。

芯片选型,即选取一款合适的芯片用于本方案的电机驱动控制。

为此推荐一款国产芯片--武汉芯源半导体的CW32!

1、CW32芯片选型

武汉芯源半导体官网:www.whxy.com

5.png

武汉芯源半导体有限公司

武汉芯源半导体有限公司,于2018年8月28日成立,为上市公司武汉力源信息技术股份有限公司全资子公司,专业负责自研芯片的研 发、设计、推广、销售及技术服务相关业务。目前已推出微处理器MCU、小容量存储芯片EEPROM、功率器件SJ-MOSFET三大系列 产品,公司致力于成为国产芯片产业的领航者。

一、基本情况

1、2018年成立 为加大自研芯片研发力度, 2018年升为独立的芯片设计公司

2、10年+ 团队成员平均从业时间超10年, 理论扎实、技术过硬

3、背靠上市公司 武汉力源信息技术股份有限公司

4、成功转型 前身为头部技术型分销企业力源信息的IC业务部, 转型为拥有经验丰富的芯片设计团队

5、业务范畴 专注芯片的设计、研发、销售及技术服务, 拥有一支专业高效的市场及销售团队

二、CW32家族

1.通用高性能的CW32F系列MCU

2.安全超低功耗CW32L系列MCU

3.无线射频MCU

4.无线射频蓝牙系列MCU

5.无线射频ChirpIoT系列MCU

6.小容量存储芯片EEPROM产品

所以我们本次选择的是CW32F030C8T6这块芯片,用来开发水泵项目。

内核:ARM(®) Cortex(®)-M0+ 最高主频64MHz

工作温度:-40℃ 至 105℃;工作电压:1.65V 至 5.5V

存储容量: 64K 字节FLASH,数据保持25年@85℃ 8K 字节RAM,支持奇偶校验

对于我们开发人员来说,一款芯片对于项目开发及维护的重要性,就好比心脏和大脑对人的重要性。如果芯片选型和售后、芯片总类和优化、迭代不够好的话,我们是不会考虑采用的。但是本次使用的CW32是一个各方面都很优秀的产品。

三、项目流程

1、设计电路原理图和PCB布局:

首先,根据项目需求,设计无刷水泵电机的电路原理图,并进行PCB布局。确保电路板的布局合理,信号和电源线路分离,减少干扰。

2、选购和连接器件:

根据设计的电路原理图,选购所需的电子器件,并按照原理图进行连接。确保连接正确,信号和电源线路接触良好,以免出现接触不良或者短路的情况。

3、烧录程序:

将编写好的控制程序烧录到cw32主控芯片中。确保程序烧录成功,并进行相应的调试,确保程序可以正确运行。

4、调试和测试:

连接相应的电源和信号源,进行系统的调试和测试。通过调整参数和监测输出,判断无刷水泵电机的运行是否符合预期,并进行必要的调整。

来源:CW32生态社区

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

围观 17

一、前言

无刷水泵被广泛应用于许多领域,如工业生产、农业灌溉以及家庭应用。它具有高效、可靠和节能的特点,因此备受青睐。但是无刷水泵的供电是一个问题。怎么能随时随地的使用无刷水泵呢?总不能拿一个水泵,还要配一台发电机吧。所以本次我们使用电动车供电,可以满足用户骑车到哪,水泵都可以使用的特点。

在户外的各种场景下,肯定要选择一颗稳定的抗干扰的芯片才行。强烈推荐CW32系列芯片。抗干扰能力值得您的信赖!

CW32F030C8T6支持工作温度: -40℃ 至 105℃;

工作电压:1.65V 至 5.5V

存储容量:  64K 字节FLASH,数据保持25年@85℃ 8K 字节RAM,支持奇偶校验,128字节OTP存储器

使用一颗好芯片,为您照亮开发之路!

二、CW-W88水泵通用控制板-方案介绍

1、简介

CW-W88水泵通用控制板适用于水泵电机抽水控制应用。配合水泵电机,可使用48V或60V电池组(电动车电池组)抽水使用。使用前请将电池充满(电池至少在一半电量以上使用)。控制板具有欠压保护、过压保护、过流保护、无水停止保护、堵转保护,速度异常等功能。可广泛应用于各类水泵抽水控制。

2、系统功能

  • 电源接线:红色,黑色接电源,不分正负,建议按使用习惯接:红色接正、黑色接负。

  • 相线接线:UVW随机接入电机的相线,试看电机运行方向。如需要反转,随意调换两根相线,重新上电即可。

  • 电源电压接入不能超过70V。上电2S后,电机启动开始抽水运行(可根据需求缩短上电稳定时间)。速度增到最大功率以后,按设定的最大功率值控制运行。如果用户电机功率达不到设定的功率,控制板则按电机的最大输出能力输出运行。

  • 电机在无水检测或其它任何故障时,会自动停止运行,且以指闪灯的闪烁频率指示故障状态。

3、工作参数

  • 型号:CW-W88

  • 使用环境:48V或60V电池组

  • 工作电压:40V~60V

  • 工作电流:48V电池组13A、60V电池组10A

  • 最大功率:600W

  • 控制方式:电流闭环

  • 尺寸:板子为圆形,直径8CM,高度3CM。建议至少加3MM厚的散热器

4、项目组成部分:

a. CW32主控芯片:

这是整个项目的关键组成部分,它负责控制无刷水泵电机的运行和参数调节。CW32主控芯片具有高性能和低功耗的特点,可以满足无刷水泵电机控制器项目的需求。

b. 无刷水泵电机

无刷水泵电机是项目的核心部分,它可以通过电磁感应实现转子转动的无刷电机效果。无刷水泵电机具有较高的效率和可靠性,适用于各种应用场景。

c. 硬件电路:

包含驱动电路,电流采集电路,母线电压采集电路,反电动势采集电路,电源电路。

d. 软件编写:

包含电机驱动代码,高压,低压保护及自恢复。电流保护,速度异常保护,空载保护,启动堵转保护。

三、项目设计说明

1、硬件设计及PCB制板

此线控器是基于武汉芯源半导体推出的CW32F030C8T6产品设计研发而成的,其主要包括MCU、电源部分、电流采样、电压采样、EG3014驱动、反电动势采集等部分,设计系统原理框图如图1所示:

1.png

  • 该控制器功能包含:

  1. 上电自启动,缓慢加速。

  2. 启动堵转保护

  3. 速度异常保护

  4. 过欠压自恢复保护

  5. 电流保护

  6. 空载保护

2.设计原理图及PCB板实物图

2.png3.png

4.png

5.png

6.png

3.软件部分

软件部分主要分为以下几部分内容,软件构成如图7所示:

7.png

a. 状态机代码

switch(USER_sSysStateErr.sSystemState)//状态机{
case STATE_IDLE_A: MDS_v_charge();break;//充电
case STATE_INIT_B: MDS_v_Init();break;//初始化
case STATE_START_C: MDS_v_Start_the_judgment(); break;//等待启动信号
case STATE_ALIGNMENT_D: MDS_v_The_motor_starts();break;//启动
case STATE_ADCLOOP_E:MDS_v_OPEN_starts();break; // 开环模式
case STATE_BRAKE_F:MDS_v_brake();break; // 刹车
case STATE_FAULT_G:MDS_v_Misjudgment();break; // 错误判断
case STATE_WAIT_L:
MDS_v_Error_recovery(USER_sSysStateErr.sSysTime.u32ReStartTime);
break; // 错误恢复判断
case STATE_STOP_M:Stop_Motor();break; // 停止
default:break;}

b. 电流电压采集计算代码

void V_A_calculation(void)
{
    USER_sVoltageAll.s16Udc=(float)(SampleData[2]*UPDS_resistance_compute);        
    keep_adcvlaue+=SampleData[4];        
    keep_adcvnum++;        
    if(keep_adcvnum>2)        
    {                
        keep_adcvnum=0;        
        keep_adcvlaue=keep_adcvlaue/2;        
        USER_sCurrentAll.s32IdcUse=(SampleData[4]*UPDS_Current_compute)/10;//100mA        
        USER_sCurrentAll.IdcValue=(float)USER_sCurrentAll.s32IdcUse/100.0;        
    }
}

c. 电机启动代码,判断启动是否失败

//(3):电机启动代码,判断启动是否失败。
void Sensorless_MOTOR_START(void)
{        
    unsigned char coun = 0;        
    USER_BLDC.HALLcountS = 0;        
    USER_BLDC.QDPwm = 20; // 启动占空比        
    do        
    {                
        if (Sensorless_START() == 0)                
        {                        
            coun++;                        
            USER_BLDC.QDPwm += 5;                
        }                
        else                        
            break;        
    } while (coun < 5 && ErrorCode == 0);        
    if (coun >= 5 && ErrorCode == 0)        
    {                
        ErrorCode = 3;        
    } // 启动失败
}

d. 电机运行代码

//(4):电机运行代码
void ADCS_chuli(void)
{        
    static unsigned char cou = 0;        
    unsigned char hx = 0;        
    unsigned int thre = 0;        
    if (USER_BLDC.Sta != 2)                
        return;        
    BEMFConvertedValue = SampleData[TAB_BEMFChannel[Forward_rotation][bHallStartStep1]];        
    RisingFalling = TAB_RFling[Forward_rotation][bHallStartStep1];
        
   if (USER_BLDC.Tonoroff == 0)                
       thre = 50; // 30;//248;  //OFF时刻采集。0.2V阀值248        
   else                
       thre = SampleData[2] / 2;        
   if (RisingFalling == FALLING) // 下降沿        
   {                
       if (BEMFConvertedValue < thre)                
       {                        
           cou++;                        
           if (cou >= 2)                        
           {                                
               cou = 0;                                
               USER_BLDC.Sta = 3;                        
               USER_BLDC.StCountComm++; // 正确检测到第三相反电动势                                
               FFlag = 1;                                
               hx = 1;                        
           }                
       }                
       else                
       {                        
           cou = 0;                
       }        
   }        
   else if (RisingFalling == RISING)        
   {                
       if (BEMFConvertedValue > thre)                
       {                        
           cou++;                        
           if (cou >= 2)                        
           {                                
               cou = 0;                                
               USER_BLDC.Sta = 3;                        
               USER_BLDC.StCountComm++; // 正确检测到第三相反电动势                                
               FFlag = 1;                                
               hx = 1;                        
           }                
       }                
       else                
       {                        
           cou = 0;                
       }        
   }        
   if (USER_BLDC.StCountComm >= STCount && USER_BLDC.StOk == 0) // 连续检测到固定数量的过零时,认为启动成功        
   {                
       USER_BLDC.StOk = 1;        
   }        
   if (USER_BLDC.StOk == 1 && hx == 1)        
   {                
       hx = 0;                
       BTIM_SetAutoreload(CW_BTIM3, USER_BLDC.StepTime / 5);                
       BTIM_SetCounter(CW_BTIM3, 0);                
       BTIM_Cmd(CW_BTIM3, ENABLE);        
   }
}

e. 电机报警代码及其自恢复功能

//(5):电机报警代码及其自恢复功能

void US_vSysErrorCheck(void)
{        
    US_vSysCurrentCheck();                  // 电流报警        
    US_vSysVoltageCheck();                  // 电压报警        
    US_vSysStuck_in();                          // 启动堵转保护        
    US_vSysSPEED_ERRORCheck();          // 速度异常保护        
    US_vSyscarrying_idlerCheck(); // 空载保护
}

void MDS_v_Error_recovery(uint32_t WaitTime) // 错误恢复
{        
    static uint32_t lg_u32WaitCount = 0;        
    if (lg_u32WaitCount >= WaitTime)        
    {                
        USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                
        USER_sSysStateErr.sSystemState = STATE_INIT_B;                
        USER_sSysStateErr.uSystemError.OverGUdcFlag = false;                
        USER_sSysStateErr.uSystemError.OverLUdcFlag = false;                
        USER_sSysStateErr.uSystemError.overStuck_in = false;                
        USER_sSysStateErr.uSystemError.GlobalErrorFlag = false;                
        ErrorCode = 0;                
        lg_u32WaitCount = 0;        
    }
    else if (((USER_sVoltageAll.s16Udc < UPDS_UDC_PROTECT_MAX_V_recover) && (USER_sSysStateErr.uSystemError.OverGUdcFlag == true)) || ((USER_sVoltageAll.s16Udc > UPDS_UDC_PROTECT_MIN_V_recover) && (USER_sSysStateErr.uSystemError.OverLUdcFlag == true))  || (USER_sSysStateErr.uSystemError.overStuck_in == true))        
    {                
        lg_u32WaitCount++;        
    }        
    else                
        lg_u32WaitCount = 0;
}

f. 电机反电动势及运行代码

反电动势过零检测法基本原理:

忽略电动机电枢反应,无刷直流电动机在稳态运行过程中,通过检测关断相的反电动势过零点获得转子的位置信号,进行对逆变器开关导通顺序切换,控制电机运动。

较为典型的 BLDCM 的无位置传感器控制方法有基于反电势的检测法、磁链估计法、电感检测法等,在该系统中采用反电势过零检测法。

反电动势过零检测法是以直流无刷电机中性点电压为基准进行反电动势过零检测的,属于间接反电动势检测方法。该方法将端电压作分压滤 波处理得到直流无刷电机的位置信号,由于有滤波电路存在,获得的位置信号比真实的反电动势过零点延时了一定的角度。

在理想情况下,电机三相绕组反电势 Ea 、Eb、Ec 的波形为梯形波。无刷电机采用 120°导通方式,在任何时刻只有两相导通,这两相电流的大小 相等、方向相反,另一相电流为零,故三相电流的 总和为零。

8.png

11.png

表: 正向反电动势过零点变化

g. 反电动势部分代码

//反电动势部分代码
void ADCS_chuli(void)
{        
    static unsigned char cou = 0;        
    unsigned char hx = 0;        
    unsigned int thre = 0;
   
   if (USER_BLDC.Sta != 2)                
       return;
   
   BEMFConvertedValue = SampleData[TAB_BEMFChannel[Forward_rotation][bHallStartStep1]];        
   RisingFalling = TAB_RFling[Forward_rotation][bHallStartStep1];
        
   if (USER_BLDC.Tonoroff == 0)                
       thre = 50; // 30;//248;  //OFF时刻采集。0.2V阀值248        
   else                
       thre = SampleData[2] / 2;
        
   if (RisingFalling == FALLING) // 下降沿        
   {                
       if (BEMFConvertedValue < thre)                
       {                        
           cou++;                        
           if (cou >= 2)                        
           {                                
               cou = 0;                                
               USER_BLDC.Sta = 3;
                                
           USER_BLDC.StCountComm++; // 正确检测到第三相反电动势                                
           FFlag = 1;                                
           hx = 1;                        
         }                
      }                
      else                
      {                        
         cou = 0;                
      }        
    }        
    else if (RisingFalling == RISING)        
    {                
        if (BEMFConvertedValue > thre)                
        {                        
            cou++;                        
            if (cou >= 2)                        
            {                                
                cou = 0;                                
                USER_BLDC.Sta = 3;
                                
           USER_BLDC.StCountComm++; // 正确检测到第三相反电动势                                
           FFlag = 1;                                
           hx = 1;                        
        }                
      }                
      else                
      {                        
          cou = 0;                
      }        
   }
        
   if (USER_BLDC.StCountComm >= STCount && USER_BLDC.StOk == 0) // 连续检测到固定数量的过零时,认为启动成功        
   {                
       USER_BLDC.StOk = 1;        
   }
  
   if (USER_BLDC.StOk == 1 && hx == 1)        
   {                
       hx = 0;                
       BTIM_SetAutoreload(CW_BTIM3, USER_BLDC.StepTime / 5);                
       BTIM_SetCounter(CW_BTIM3, 0);                
       BTIM_Cmd(CW_BTIM3, ENABLE);        
   }
}

h. 母线电压监测-分压电路原理及代码

可以看原理图中的分压电路是两个电阻

i. 软件保护

void US_vSysErrorCheck(void)
{        
    US_vSysCurrentCheck();                  // 电流报警        
    US_vSysVoltageCheck();                  // 电压报警        
    US_vSysStuck_in();                          // 启动堵转保护        
    US_vSysSPEED_ERRORCheck();          // 速度异常保护        
    US_vSyscarrying_idlerCheck(); // 空载保护
}

1):电流保护

在无刷电机运行中,有时候堵转很容易烧mos管,所以电流保护就尤为的重要。

在程序中配置了电流触发保护值,是15A,因为我们额定功率是500W。还有电流采集电路没有加入运放,采集不精确,所以需要把电流值设置大一点。之后也要根据实际的调试而改变。

//电流保护
void US_vSysCurrentCheck(void) // 电流保护
{
    #if (UPDS_ISUM_DETECTION == 1)        
    if ((USER_sCurrentAll.IdcValue) >= UPDS_OVERISUM_limit_MAX) // 一级电流保护        
    {                
        USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount++;                
        if (USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount >= UPDS_OVERISUM_limit_MS)                
        {                        
            USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount = UPDS_OVERISUM_limit_MS;                        
            USER_sSysStateErr.uSystemError.OverIdcFirstlevelFlag = true;                        
            USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                        
            USER_sSysStateErr.sSystemState = STATE_FAULT_G;                
        }        
    }        
    else        
    {                
        USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount--;                
        if (USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount <= 0)                
        {                        
            USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount = 0;                        
            USER_sSysStateErr.uSystemError.OverIdcFirstlevelFlag = false;                
        }        
    }
#endif
}

/********************限流保护值***********************/ 
#define UPDS_OVERISUM_limit_MAX         (15) //unit: A 限流保护值 
#define UPDS_OVERISUM_limit_MS           (500)//unit: ms  触发保护时间

/**********************报警设置********************/
#define  UPDS_ISUM_DETECTION                         (1)                //电流保护使能

2):电压保护

在高压和低压保护中,我们在程序中加入了自恢复功能,如果电压高于58v就会触发高压保护,电机停止,等待电压下降到55v以下,就会触发自恢复功能,等待自恢复时间结束,电机重新启动。低压也是同理。

//电压保护
void US_vSysVoltageCheck(void) // 电压保护
{
    #if (UPDS_UDC_DETECTION == 1)        if (USER_sVoltageAll.s16Udc > UPDS_UDC_PROTECT_MAX_V)        
    {                
        USER_sSysStateErr.sSysTime.s32OverGUdcCount++;                
        if (USER_sSysStateErr.sSysTime.s32OverGUdcCount >= UPDS_OVERVOLTAGE_VOLTAGE_MS)                
        {                        
            USER_sSysStateErr.sSysTime.s32OverGUdcCount = UPDS_OVERVOLTAGE_VOLTAGE_MS;                        
            USER_sSysStateErr.uSystemError.OverGUdcFlag = true;                        
            USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                        
            USER_sSysStateErr.sSystemState = STATE_FAULT_G; // 高压保护                
        }        
    }        
    if (USER_sVoltageAll.s16Udc < UPDS_UDC_PROTECT_MIN_V && USER_sVoltageAll.s16Udc > 5)        
    {                
        USER_sSysStateErr.sSysTime.s32OverLUdcCount++;                
        if (USER_sSysStateErr.sSysTime.s32OverLUdcCount >= UPDS_UNDERVOLTAGE_VOLTAGE_MS)                
        {                        
            USER_sSysStateErr.sSysTime.s32OverLUdcCount = UPDS_UNDERVOLTAGE_VOLTAGE_MS;                        
            USER_sSysStateErr.uSystemError.OverLUdcFlag = true;                        
            USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                        
            USER_sSysStateErr.sSystemState = STATE_FAULT_G; // 低压保护                
        }        
    }
#endif
}

/**********************报警设置********************/
#define  UPDS_UDC_DETECTION                           (1)                //电压保护使能

/********************电压保护值***********************/
#define UPDS_UDC_REAL                                         (48.0f) //unit: V;电压源电压
#define UPDS_UDC_PROTECT_MAX_V                        (58.0f)        //unit: V;过压值
#define UPDS_UDC_PROTECT_MAX_V_recover        (54.0f) //unit: V;过压恢复值
#define UPDS_OVERVOLTAGE_VOLTAGE_MS         (10.0)  //Unit: ms 过电压持续时间1ms基准
#define UPDS_UDC_PROTECT_MIN_V                        (30.0f)        //unit: V;欠压值
#define UPDS_UDC_PROTECT_MIN_V_recover        (32.0f)        //unit: V;欠压恢复值
#define UPDS_UNDERVOLTAGE_VOLTAGE_MS         (14.0)  //Unit: ms 欠电压持续时间1ms基准

3):启动堵转保护

在堵转保护之中,我们同样设置了自恢复启动,但是自恢复启动有次数,只有五次。如果5次都没有启动成功,电机将停止转动。不会启动。

/启动堵转保护void US_vSysStuck_in(void) // 堵转保护启动5次
{        
    if ((USER_stuck_in.locked_rotor_flag == 1) || (ErrorCode == 3)) // 检测如果触发了一次堵转保护之后 电流大于额定值都没有启动        
    {                
        USER_stuck_in.locked_rotor_flag = 0;                
        USER_sSysStateErr.uSystemError.overStuck_in = true;           // 启动堵转标志                
        USER_sSysStateErr.uSystemError.GlobalErrorFlag = true; // 全局错误标志位                
        USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                
        USER_sSysStateErr.sSystemState = STATE_FAULT_G; // 跳入错误判断                
        USER_sADSampleAll.s16VSPLPF = 0;                                   // 清空一下电位器                
        USER_stuck_in.Phase_Stuck_num++;                                   // 启动堵转报警加1        
    }
}

4):速度异常保护

检测目标速度和实际速度之比,如果较小或者较大就是速度异常

//速度异常保护
float value;
void US_vSysSPEED_ERRORCheck(void) // 速度异常保护
{
    if (USER_sADSampleAll.s16VSPLPF > 2500) // 启动之后        
    {                
        value = (float)USER_sADSampleAll.s16VSPLPF / (float)USER_sADSampleAll.s32SpdUse;                
        if ((value > 1.5) || (value < 0.5))                
        {                        
            USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount++;                        
            if (USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount >= UPDS_SPEED_IDLER_MS)                        
            {                                
                USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount = UPDS_CARRYING_IDLER_MS;                                
                USER_sSysStateErr.uSystemError.over_speed_error = true;                                
                USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                                
                USER_sSysStateErr.sSystemState = STATE_FAULT_G;                        
            }                
        }                
        else                
        {                        
            USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount--;                        
            if (USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount <= 0)                        
            {                                
                USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount = 0;                                
                USER_sSysStateErr.uSystemError.over_speed_error = false;                        
            }                
        }        
    }
}

5):空载保护

空载保护是不让水泵电机空转,检测方法是检测电流大小,如果在水里,电流会比较大,只需要在速度达到某一点时,检测电流的大小就可以实现。

//空载保护void US_vSyscarrying_idlerCheck(void) // 空载保护
{        
    if (USER_sADSampleAll.s32SpdCommand > 3000) // 启动之后        
    {                
        if ((USER_sCurrentAll.IdcValue) < UPDS_CARRYING_IDLER_MAX)                
        {                        
            USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount++;                        
            if (USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount >= UPDS_CARRYING_IDLER_MS)                        
            {                                
                USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount = UPDS_CARRYING_IDLER_MS;                                
                USER_sSysStateErr.uSystemError.overload_protection = true;                                
                USER_sSysStateErr.sSystemStatePre = USER_sSysStateErr.sSystemState;                                
                USER_sSysStateErr.sSystemState = STATE_FAULT_G;                        
            }                
        }                
        else                
        {                        
            USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount--;                        
            if (USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount <= 0)                        
            {                                
                USER_sSysStateErr.sSysTime.s32OverIdcFirstlevelCount = 0;                                
                USER_sSysStateErr.uSystemError.overload_protection = false;                        
            }                
        }        
    }

3. 调试部分

下载程序即可上电调试;

接上电机的UVW三相,记得在代码中设置一下分压电阻,电流采样的电阻(如果没有更改就不需要更换)。四线插入正确,按下烧录键即可烧录成功。烧录后电机就3s缓启动正常运行起来了。需要检测电机的UVW三相,如果是如图9这样呈现梯形就可以。

9.png

10.png

4. 小结

以上即为本次无刷电机驱动板设计的全部基本内容。芯源推出的CW32F030系列产品,已全面实现-40℃ 至 105℃超宽温度范围和 1.65V~5.5V 超宽工作电压,面向最广泛的各种基础应用。用户可以根据自己的需求自行更改代码功能。

来源:CW32生态社区

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

围观 27

例程资料链接如下:

BD网盘链接:

https://pan.baidu.com/s/1JUMZH-sJAH-dPB0AtEvE7w

提取码:oe9t

一、简介 

1.薄膜式键盘是一种常见的输入设备,它由一层薄膜电路板和一层触摸膜组成。薄膜电路板上印有导电图案,而触摸膜则具有与之对应的按键区域。这种键盘的应用场景非常广泛,以下是几个典型的应用场景:

(1)电子产品:薄膜式键盘被广泛应用于各种电子产品中,如手机、平板电脑、数码相机等。由于其结构简单、体积小巧,可以很好地满足电子产品的设计需求。

(2)工业控制:在工业自动化领域,薄膜式键盘常用于控制面板和操作界面。它们具有防尘、防水、抗腐蚀等特性,能够适应恶劣的工作环境。

(3)医疗设备:医疗设备通常需要高度卫生和易清洁的特点,薄膜式键盘因其表面光滑、易擦拭的特性而被广泛应用于医疗设备中,如手术台、心电图仪等。

二、所需物料

本实验使用到了CW32-48F大学计划开发板、5*4薄膜式键盘模块、0.96寸OLED显示屏及Keil5开发环境。开发板上留有矩阵键盘接口,可以直接将模块插上使用。

1.png

CW32-48F大学计划板   

2.png

5*4薄膜式键盘

   3.png

键盘内部连线示意图

注:键盘的9根引线从左至右分别与单片机引脚PB15、PB14、PB13、PB12、PA6、PA5、PA4、PA1、PA0相连。

三、核心代码

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

#define NUM_LENGTH 6

uint8_t choose_flag=0;    //选中标识
uint8_t choose_index=0;   //数组下标
uint8_t exert_flag=0;     //执行标识

uint8_t number[NUM_LENGTH]={0};    //存储6位数字
uint8_t num_index=0;      //数组下标    
char temp='.';            //默认值'.' 

int main()
{  
    uint8_t i;  
  uint8_t position=0;     //选中的数字在数组中的位置  
  OLED_Init();            //OLED显示  
  Key_GPIO_Init();        //5*4薄膜键盘GPIO初始化  
  BTIM_Init();            //定时器初始化,控制按键扫描周期  
  while(1)  
  {     
    if(exert_flag==1)     //若执行标识已打开    
    {      
      switch(temp)      
      {        
         case '<':           //选中左移          
         if(choose_flag==0) position=choose_index+1; //向左选中数字          
         if(position!=0)                              //若已有数字输入          
         {            
             choose_flag=1;                            //打开选中标识                    
           OLED_Clear_Row(2);                         //先清除已有标识符号‘^’            
           if(--position==0) position=choose_index;  //选中左移            
           OLED_ShowChar(2,position,'^');            //显示选中标识符号'^'          
         }          
         break;        
         case '>':          
         if(choose_flag==0) position=choose_index;   //向右选中数字          
         if(position!=0)                              //若已有数字输入          
         {            
             choose_flag=1;                            //打开选中标识            
             OLED_Clear_Row(2);                        //先清除已有标识符号'^'            
             if(++position==choose_index+1) position=1;//选中右移            
             OLED_ShowChar(2,position,'^');            //显示选中标识符号'^'          
         }          
         break;        
         case 'E':         
          choose_flag=0;                              //关闭选中标识          
          OLED_Clear_Row(2);                          //清除选中标识符号'^'          
          break;        
          default:          
          if(choose_flag==0)                           //若未打开选中标识          
          {            
              choose_index=num_index+1;                              
              if(num_index==0)                                      
              {              
                  OLED_Clear_Row(1);              
                  for(i=0;i<NUM_LENGTH;i++)                        //溢出清零                
                  number[i]=0;            
              }            
              number[num_index]=temp;            
              OLED_ShowNum(1,num_index+1,number[num_index],1);            
              num_index++;                              //输入右移            
              choose_index=num_index;            
              if(num_index==NUM_LENGTH) num_index=0;          
             }          
             else          
             {            
                  number[position-1]=temp;                      //若已打开选中标识,将获取的键值保存至数组中对应的位置            
                  OLED_ShowNum(1,position,number[position-1],1);//显示屏上覆盖原有数字          
             }          
             break;          
             }        
             exert_flag=0;        //执行后关闭标识    
        }      
     }
}

void BTIM1_IRQHandler(void)     //基本定时器1中断服务函数
{  
    static uint8_t cnt = 0;   
    if(BTIM_GetITStatus(CW_BTIM1,BTIM_IT_OV))    
    {      
        if(++cnt>=18)     
        {      
            cnt=0;      
            temp=Key_Scan(); //每180ms执行一次按键扫描,返回值赋值给temp      
            if(temp!='.')  exert_flag=1;    //打开执行标识    
        }    
        BTIM_ClearITPendingBit(CW_BTIM1,BTIM_IT_OV); //清除标志位  
    }
}
Key.c:
#include "Key.h"
#include "main.h"
#include "Delay.h"
#include "OLED.h"

#define  ROW_PORT CW_GPIOA      //键盘行引脚端口
#define  COL_PORT CW_GPIOB      //键盘列引脚端口

#define  ROW_NUM  4     //4行
#define  COL_NUM  4     //4列

uint16_t row_pins[ROW_NUM]={GPIO_PIN_1,GPIO_PIN_4,GPIO_PIN_5,GPIO_PIN_6};     //每一行所对应的引脚

uint16_t col_pins[COL_NUM]={GPIO_PIN_15,GPIO_PIN_14,GPIO_PIN_13,GPIO_PIN_12}; //每一列所对应的引脚

char key_value[ROW_NUM][COL_NUM]={    //键值  
    1,   2,  3,   '(',   
    4,   5,  6,   ')',  
    7,   8,  9,   'E',  
    '<', 0,  '>', 'Y'
};

void Key_GPIO_Init(void)
{  
    __RCC_GPIOA_CLK_ENABLE();  
    __RCC_GPIOB_CLK_ENABLE();  
     
    //rows-->置行      
    GPIO_InitTypeDef GPIO_InitStruct;   
    GPIO_InitStruct.IT=GPIO_IT_NONE;  
    GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;      //推挽输出  
    GPIO_InitStruct.Pins=row_pins[0]|row_pins[1]|row_pins[2]|row_pins[3];  
    GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;  
    GPIO_Init(ROW_PORT, &GPIO_InitStruct);   
    
    //cols-->检列  
    GPIO_InitStruct.Mode=GPIO_MODE_INPUT_PULLUP;   //上拉输入  
    GPIO_InitStruct.Pins=col_pins[0]|col_pins[1]|col_pins[2]|col_pins[3];  
    GPIO_Init(COL_PORT, &GPIO_InitStruct);
}

char Key_Scan(void)
{  
    uint8_t i,j;  
    char key = '.';  //默认值'.'   
    
    for ( i = 0; i < ROW_NUM; i ++ )   //1-4行依次置低  
    {    
        GPIO_WritePin(ROW_PORT,row_pins[i],GPIO_Pin_RESET);    
        for( j = 0; j < COL_NUM; j ++ )  //依次检测1~4列电平    
        {      
            if( GPIO_ReadPin(COL_PORT,col_pins[j])==RESET )  //如果检测到低电平,则代表有按键按下      
            {        
                key = key_value[i][j];    //获取键值        
                break;            //跳出检列循环      
            }    
        }    
        GPIO_WritePin(ROW_PORT,row_pins[i],GPIO_Pin_SET);  //本行恢复高电平,准备置低下一行    
        if(key != '.') break; //若key不是默认值,则代表已检测到按键按下,退出置行循环,结束本次按键扫描  
    }   
    
    return key;  //返回键值
}

四、效果演示

4.png

5.png

6.png

7.png

来源:CW32生态社区

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

围观 15

目前截止时间不限,只要想参加,联系小助理即可参与!

/ 关于作者/

1694051305885876.png

雷超林,硬件工程师,研究方向嵌入式,电力电子;省级以上竞赛奖项7项,入选湖南省教育厅燎原计划。

一、描述

1.项目说明

使用武汉芯源半导体CW32系列MCU作为主控一款小巧的MP3。采用CW32L系列低功耗芯片。

2.项目相关功能

  1. 锂电池供电,TYPE-C接口充电,电量显示;

  2. 功耗管理;

  3. 3.5mm音频输出接口,可不设计外置扬声器模式;

  4. 屏幕显示:歌词名、歌词、电量等;可设置屏幕使用亮度、时间进行功耗管理;

  5. SD卡插入,并支持读取歌曲信息;

3.项目属性

本项目为首次公开,为本人原创项目。项目未曾在别的比赛中获奖。

4.项目进度 

5.20:开始画原理图

5.23:画PCB

5.27:焊接完毕,开始调程序

6.15第一版完成

6.18第二版开始

7.12全部完成

二、设计原理

设计框架

2.png

上图为整个MP3的主体系统框架,先是由电池提供4.2V电压给电源管理,电源管理再转化为3.3V以及1.8V的电压给整个系统,

主控将内存卡里的信息提取转为二进制输入到音频解码芯片(VS1053B)里,同时在OLED显示屏上显示信息。用独立按键可以控制播放以及暂停等信息。

设计难点

低功耗(解决方案:电源使用业界领先的电荷泵降压芯片,芯片采用CW32L系列低功耗主控)

1.主控

3.png

4.png

主控采用CW32L031C8U6,ARM® Cortex®-M0+ 32 位低功耗微控制器 64K 字节 FLASH,8K 字节 RAM,最高主频 48MHz,支持最多 39 路 I/O 接口。

2.音频解码芯片

5.png

VS1053B是一款高性能音频编解码模块,支持:MP3/WMA/OGG/WAV/FLAC/MIDI/AAC等音频格式的解码,并支持:OGG/WAV音频格式的录音,支持高低音调节以及EarSpeaker空间效果设置,功能十分强大

它包含了一个高性能、有专利的低功耗DSP 处理器内核VS_DSP4、工作数据存储器、供用户应用程序和任何固化解码器一起运行的16 KiB 指令RAM 及0.5KiB 多的数据RAM、串行的控制和输入数据接口、最多8 个可用的通用I/O引脚。

3.独立按键

6.png

7.png

独立按键采用轻触按键以及拨片按键,符合人体工程学,按起来非常舒服

4.电源管理

8.png

电源管理采用电池充电芯片TP4054,TP4054是一个完善的单片锂离子电池恒流/恒压线形电源管理芯片。它薄的尺寸和小的外包装使它便于便携用。更值得一提的是,TP4054专门设计适用于USB的供电规格。得益于内部的MOSFET结构, 在应用上不需要
外部电阻和阻塞二极管。在高能量运行和高外围温度时,热反馈可以控制充电电流以降低芯片温度。 

电源芯片采用TX4310B是一款低噪声,恒定频率(1.2MHz)开关电容器倍压器。
TX4310B从1.8V至5V输入产生稳定的输出电压。外部元件数量较少(VDD和VOUT处有一个快速电容和两个小旁路电容)使得芯片非常适用于电池供电的小型应用。
电荷泵架构可保持恒定的开关频率以实现空载稳压输出,并降低输出和输入波纹。 

5.OLED显示屏

9.png

10.png

OELD采用0.96寸 蓝色 128x64像素分辨率 单色 30PIN,主控芯片    SSD1315

6.外接接口电路

11.png

外接电路有TF卡插槽,下载接口以及3.5mm音频接口,这里我们注意TF卡采用SPI通信,用了10K的电阻进行上拉,保证通信的稳定性。

三、软件说明

12.png

程序较为复杂,采用了状态机以及文件系统,增加了息屏之后进入低功耗模式,得益于我们CW32L031系列优异的低功耗性能,可以连续播放音乐12小时,

我们在sd卡内需要放置字库文件,在我工程的附件中下载,通过读卡器写入到SD卡中才能工作,否则开机会提示

四、实物展示

13.jpg

14.jpg

15.jpg

16.jpg

来源:CW32生态社区

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

围观 13

例程资料链接如下:

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)。

围观 11

页面

订阅 RSS - CW32