天天看点

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

 最近在研究CH32V307的板子,这个板子性能还是非常强大的。CH32V307是基于32位RISC-V设计的互联型微控制器,配备了硬件堆栈区、快速中断入口,在标准RISC-V基础上大大提高了中断响应速度。加入单精度浮点指令集,扩充堆栈区,具有更高的运算性能。

看看CH32V307在物联网控制方面的应用如何,这就就做了一些相关的测试。使用CH32V307和ESP8266实现阿里云物联网平台的连接,并能远程控制CH32V307开发板上的LED灯的通断。

首先要建立的是下位机的设计,这里就不再赘述如何串口烧录ESP8266的AT固件了,网上有很多可以参考的设计。

现在有MQTT固件,使用也简答。我没有使用这类固件,使用的是正常的AT固件。通过TCP建立连接,然后与MQTT broker联系的。

CH32V307与ESP8266使用的是串口2的通信,这里需要配置波特率为115200,并开启相关的中断。

void USART2_CFG(void)

{

    GPIO_InitTypeDef  GPIO_InitStructure = {0};

    USART_InitTypeDef USART_InitStructure = {0};

    NVIC_InitTypeDef  NVIC_InitStructure = {0};



    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 , ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);



    /* USART2 TX-->A.2   RX-->A.3 */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    GPIO_Init(GPIOA, &GPIO_InitStructure);



    USART_InitStructure.USART_BaudRate = 115200;

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    USART_InitStructure.USART_StopBits = USART_StopBits_1;

    USART_InitStructure.USART_Parity = USART_Parity_No;

    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;



    USART_Init(USART2, &USART_InitStructure);

    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);





    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);





    USART_Cmd(USART2, ENABLE);

}
           

并将指令发送到ESP8266和接收ESP266的数据

void USART2_IRQHandler(void)

{

    u8 uartR=0;

    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)

    {

        uartR= USART_ReceiveData(USART2);

        if(USART1_RX_STA<USART1_MAX)  //还可以接收数据

       {

           usart1_rxbuf[USART1_RX_STA++]=uartR;   //记录接收到的值

       } else

       {

           USART1_RX_STA=0;                //计数归零

       }



    }

}
           

初始化ESP8266的时候需要,配置ESP8266位STA模式并与路由器建立连接。

uint8_t ESP8266_Init(void)

{

    //清空发送和接收数组

    memset(usart1_txbuf,0,sizeof(usart1_txbuf));

    memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));



    ESP8266_ExitUnvarnishedTrans();                //退出透传

    Delay_Ms(500);

    ESP8266_ATSendString("AT+RST\r\n");

    Delay_Ms(2000);

    if(ESP8266_Check()==0)              //使用AT指令检查ESP8266是否存在

    {

        return 0;

    }

    memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));    //清空接收缓冲

    ESP8266_ATSendString("ATE1\r\n");             //关闭回显

    if(FindStr((char*)usart1_rxbuf,"OK",500)==0)  //设置不成功

    {

        return 0;

    }

    return 1;                         //设置成功

}
           

这里用到是strstr的指令,判断是否有我们要求的返回值。FindStr这里的代码是这样规划的。

通过多次演示,判断是否收到数据,如果没有收到数据或者不一样的数据,则判断不正确。

uint8_t FindStr(char* dest,char* src,uint16_t retry_nms)

{

    int retry_nms_t = retry_nms;                   //超时时间

    while(strstr((char*)usart1_rxbuf,src)== NULL && retry_nms_t)//等待串口接收完毕或超时退出

    {

        Delay_Ms(10);retry_nms_t--;

    }

    printf(usart1_rxbuf);

    if(retry_nms_t>=0) return 1;



    return 0;

}
           

然后就是需要去连接WIFI模块了。需要提前写入路由器的名称和代码

uint8_t ESP8266_ConnectAP(char* ssid,char* pswd)

{

    uint8_t cnt=5;

    while(cnt--)

    {

        memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));

        ESP8266_ATSendString("AT+CWMODE_CUR=1\r\n");              //设置为STATION模式

        if(FindStr((char*)usart1_rxbuf,"OK",200) != 0)

        {

            break;

        }

    }

    if(cnt == 0)

        return 0;

    cnt=2;

    while(cnt--)

    {

        memset(usart1_txbuf,0,sizeof(usart1_txbuf));//清空发送缓冲

        memset(usart1_rxbuf,0,sizeof(usart1_rxbuf));//清空接收缓冲

        sprintf((char*)usart1_txbuf,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pswd);//连接目标AP

        ESP8266_ATSendString((char*)usart1_txbuf);

        if(FindStr((char*)usart1_rxbuf,"GOT IP",8000)!=0)                      //连接成功且分配到IP

        {

            return 1;

        }

    }

    return 0;

}
           

在编写代码之前,我们要先搭建好阿里云物联网平台,建立产品和设备。

要建立设备,设备名称随便起。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

建立产品以后,需要建立的是物模型。因为我们只是做一个简单的LED的控制和状态显示,所以只添加了一个LED即可。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

在产品里面就是需要建立设备了,这里设备名称随便起,但是必须是英文,后面需要用到的。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

点击查看设备的三元素,填写到程序中,生产用户名 ID和密码。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

我们接着回复编写我们的程序,连接阿里云。

这里需要的是阿里云的IP地址,https://help.aliyun.com/product/30520.html 具体的应用框架,可以看到这里。

if(ESP8266_ConnectServer("TCP",ServerIP,1883)!=0)

    {

        printf("Conn to MQTT Server !\r\n");

    }

    else printf("Connto MQTT Fail!\r\n");
           

这里是生成三要素的代码。

void AliIoT_Parameter_Init(void)

{        

        char temp[128];                                                       //计算加密的时候,临时使用的缓冲区



        memset(ClientID,128,0);                                               //客户端ID的缓冲区全部清零

        sprintf(ClientID,"%s|securemode=3,signmethod=hmacsha1|",DEVICENAME);  //构建客户端ID,并存入缓冲区

        ClientID_len = strlen(ClientID);                                      //计算客户端ID的长度

        

        memset(Username,128,0);                                               //用户名的缓冲区全部清零

        sprintf(Username,"%s&%s",DEVICENAME,PRODUCTKEY);                      //构建用户名,并存入缓冲区

        Username_len = strlen(Username);                                      //计算用户名的长度

        

        memset(temp,128,0);                                                                      //临时缓冲区全部清零

        sprintf(temp,"clientId%sdeviceName%sproductKey%s",DEVICENAME,DEVICENAME,PRODUCTKEY);     //构建加密时的明文   

        utils_hmac_sha1(temp,strlen(temp),Passward,DEVICESECRE,DEVICESECRE_LEN);                 //以DeviceSecret为秘钥对temp中的明文,进行hmacsha1加密,结果就是密码,并保存到缓冲区中

        Passward_len = strlen(Passward);                                                         //计算用户名的长度

        

        memset(ServerIP,128,0);  

        sprintf(ServerIP,"%s.iot-as-mqtt.cn-shanghai.aliyuncs.com",PRODUCTKEY);                    //构建服务器域名

        //sprintf(ServerIP,"106.54.182.59");                    //构建服务器域名

        ServerPort = 1883;    
           

至于如何使用MQTT连接 Broker,这里也不赘述了,有代码,直接应用即可。

MQTT_Connect(ClientID, Username, Passward)
           

连接以后,可以看到,设备已经显示上线了。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

我们在串口上也可以看到输出的日志。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

这里是定时上传LED状态到阿里云物联网平台的。固定的json格式,不能修改,根据前面定义的物模型编写的。

            if(Connnect_flag==1) {//有客户端连接,发送到客户端

                sprintf(s_temp,"{\"id\":1,\"params\":{\"led\":%d},\"version\":\"1.0\",\"method\":\"thing.event.property.post\"}",

                       led_state);

                MQTT_PublishData(P_TOPIC_NAME,s_temp,0);

            }
           

这里显示的就是LED的状态。当前设备为关闭状态。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

我们可以使用设备模式实现控制指令的下发。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

当我们选择LED为关闭或者开启状态的时候,在单片机上就会收到这样的数据。

[RISC-V MCU 应用开发]基于CH32V307的物联网远程控制

里面json数据"\"led\":1"或者"\"led\":0"便是对LED的状态控制。

我们在程序连判断一下,接收到数据是哪个,就可以对相应的LED进行控制了

if(strstr(usart1_rxbuf+4,"\"led\":1"))led_state=1;

            else if(strstr(usart1_rxbuf+4,"\"led\":0"))led_state=0;

            if(led_state==0)GPIO_SetBits(GPIOE,GPIO_Pin_5);

            else GPIO_ResetBits(GPIOE,GPIO_Pin_5);
           

来看看开发板的验证吧。绿色框就是LED状态的变化。附上源代码

---------------------

作者:51xlf

链接:https://bbs.21ic.com/icview-3239654-1-1.html

来源:21ic.com

此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

继续阅读