本帖最后由 qiuxuezhe1 于 2020-6-16 21:14 编辑

【富芮坤物联网开发板评测】通过BLE控制步进电机,手机回传显示信息


一、鸣谢:
滴水之恩当涌泉相报,首先感谢由富芮坤冠名,面包板社区主办的原创物联网开发板设计大赛提供的这次机会,感谢上海富芮坤微电子有限公司为本次活动提供了100FR8016HA物联网开发板,同时感谢面包板社区以及各位大神们提供的学习平台。别问我为何看得远,因为我站在了巨人的肩上。

二、目标:
通过手机蓝牙控制步进电机的正反转和停止(本来想设计一个智能玩具车,因为配件不全仅开发其控制和驱动的关键技术),并将温度和湿度回传手机显示。
开发的视频如下:
https://v.qq.com/x/page/l3080suxse6.html?vuid24=PLFtlYEoBNHSG83kEXjb2Q%3D%3D&url_from=share&second_share=0&share_from=copy

三、开发板初次见面:
开发板靓照,打开包装
图片1.png

正反面的靓照:
图片2.png
通电检查:
图片3.png


四、工程开发

1搜集开发板的设计资料:
资料下载地址1https://mbb.eet-china.com/download/203375.html
资料下载地址2http://www.freqchip.com/gjhkfb
官方网站:https://www.freqchip.com
SDK下载:https://www.freqchip.com/sdkxz
本人的开发板是1.3版本所以重点关注(开发板资料---硬件版本v1.3freqchip-FR801xH-SDK-masterSDK文件)。消化硬件和烧写程序的资料,因为KEIL比较常用,所以这里就不再啰嗦了,直接进入正题。

上电初次体验:首先安装USB驱动,CP210x_Windows_Drivers_with_Serial_Enumeration
图片4.png


使用freqchip-FR801xH-SDK-master\FR801xH-SDK\tools\FR8010H_Download_Tool.exe烧录SDK文件

2)硬件更改
学习FR8016HA 开发板使用手册,和FR8016HA_Dev_V1.3开发板原理图.pdf按照下图I/O外界跳线,进行跳线短接,此处K2需要短接到PA1,其余9处用短接帽短接。
图片5.png
步进电机:28BYJ-48   四相五线步进电机
驱动方式如下图,通过示波器观察PD4PD6波形,在此注意点PWM的周期不能太短,不然容易失步,我在此就犯了一个低级错误。
图片6.png

3)手机APP设置
手机APP下载搜索”蓝牙调试器”安装,打开允许蓝牙接通,设备连接找到自己的开发板,名称中Simple Per的那个,点击加号等待连接。点击专业调试,点击加号创建工程-智能玩具车,通信设置发送数据和接收数据如下图所示,编辑控件添加文本框链接到温湿度,添加按钮链接到正转反转和停止如下图所示。为了方便我将正传设置为2,反转设置为3,停止设置为4,获取环境参数设置为5。发送用的是字节,接收用的是float
图片7.png
图片8.png

4) 软件更改:
此部分为大头工作,一步错可能要走很多的弯路,所以我尽量把所有改动的代码贴出来。
因为步进电机要使用PD4PD5PD6PD7,首先要将按键K2占用的PD6空出来,我选择了PA1
更改文件1在文件ble_simple_peripheral.c中的头文件更改如下:
#include <stdbool.h>
#include "gap_api.h"
#include "gatt_api.h"
#include "driver_gpio.h"
#include "driver_pmu.h"
#include "button.h"
#include "os_timer.h"
#include "speaker_service.h"
#include "simple_gatt_service.h"
#include "ble_simple_peripheral.h"

#include "sys_utils.h"
#include "lcd.h"
#include "capb18-001.h"
#include "sht3x.h"
#include "decoder.h"
#include "gyro_alg.h"
#include "driver_pwm.h"

函数void simple_peripheral_init(void)中更改如下:

//        pmu_set_pin_pull(GPIO_PORT_D, (1<<GPIO_BIT_6), true);
//        pmu_set_pin_pull(GPIO_PORT_C, (1<<GPIO_BIT_5), true);
//        pmu_port_wakeup_func_set(GPIO_PD6|GPIO_PC5);
//        button_init(GPIO_PD6|GPIO_PC5);
                pmu_set_pin_pull(GPIO_PORT_A, (1<<GPIO_BIT_1), true);
                pmu_set_pin_pull(GPIO_PORT_C, (1<<GPIO_BIT_5), true);
                pmu_port_wakeup_func_set(GPIO_PA1|GPIO_PC5);
                button_init(GPIO_PA1|GPIO_PC5);


更改文件2在文件proj_main.c中的头文件更改如下:
#include <stdio.h>
#include <string.h>
#include "gap_api.h"
#include "gatt_api.h"
#include "os_timer.h"
#include "os_mem.h"
#include "sys_utils.h"
#include "button.h"
#include "jump_table.h"
#include "user_task.h"
#include "driver_plf.h"
#include "driver_system.h"
#include "driver_i2s.h"
#include "driver_pmu.h"
#include "driver_uart.h"
#include "driver_rtc.h"
#include "ble_simple_peripheral.h"
#include "simple_gatt_service.h"
#include "driver_pwm.h"

文件中为了产生8拍的步进电机驱动信号需要设置PWM波形,函数void user_entry_before_ble_init(void)更改新增如下:
                system_set_port_mux(GPIO_PORT_D,GPIO_BIT_4,PORTD4_FUNC_PWM4);
                system_set_port_mux(GPIO_PORT_D,GPIO_BIT_5,PORTD5_FUNC_PWM5);
                system_set_port_mux(GPIO_PORT_D,GPIO_BIT_6,PORTD6_FUNC_PWM0);
                system_set_port_mux(GPIO_PORT_D,GPIO_BIT_7,PORTD7_FUNC_PWM1);
                pwm_init(PWM_CHANNEL_4,50,62);
                pwm_init(PWM_CHANNEL_5,50,62);
                pwm_init(PWM_CHANNEL_0,50,62);
                pwm_init(PWM_CHANNEL_1,50,62);

更改文件3为了使用手机APP需要设置通讯协议格式,在文件simple_gatt_service.c中新增如下函数:

#define PACK_HEAD 0xa5   
#define PACK_TAIL 0x5a
unsigned char *valuepack_tx_buffer;
unsigned short valuepack_tx_index;
unsigned char valuepack_tx_bit_index;
unsigned char valuepack_stage;

void startValuePack(unsigned char *buffer)
{
        valuepack_tx_buffer = buffer;
        valuepack_tx_index = 1;
        valuepack_tx_bit_index = 0;
        valuepack_tx_buffer[0] = PACK_HEAD;
        valuepack_stage = 0;
}
void putBool(unsigned char b)
{
if(valuepack_stage<=1)
{
   if(b)
      valuepack_tx_buffer[valuepack_tx_index] |= 0x01<<valuepack_tx_bit_index;
         else
                        valuepack_tx_buffer[valuepack_tx_index] &= ~(0x01<<valuepack_tx_bit_index);

  valuepack_tx_bit_index++;
        if(valuepack_tx_bit_index>=8)
        {
                valuepack_tx_bit_index = 0;
                valuepack_tx_index++;
        }
        valuepack_stage = 1;
}
}


void putByte(char b)
{
if(valuepack_stage<=2)
{
        if(valuepack_tx_bit_index!=0)
        {        
                valuepack_tx_index++;
          valuepack_tx_bit_index = 0;
        }
        valuepack_tx_buffer[valuepack_tx_index] = b;
        valuepack_tx_index++;

        valuepack_stage = 2;
}
}
void putShort(short s)
{
if(valuepack_stage<=3)
{
                if(valuepack_tx_bit_index!=0)
        {        
                valuepack_tx_index++;
          valuepack_tx_bit_index = 0;
        }
        valuepack_tx_buffer[valuepack_tx_index] = s&0xff;
        valuepack_tx_buffer[valuepack_tx_index+1] = s>>8;

        valuepack_tx_index +=2;
        valuepack_stage = 3;
}
}
void putInt(int i)
{
if(valuepack_stage<=4)
{
                if(valuepack_tx_bit_index!=0)
        {        
                valuepack_tx_index++;
          valuepack_tx_bit_index = 0;
        }

        valuepack_tx_buffer[valuepack_tx_index] = i&0xff;
        valuepack_tx_buffer[valuepack_tx_index+1] = (i>>8)&0xff;
        valuepack_tx_buffer[valuepack_tx_index+2] = (i>>16)&0xff;
        valuepack_tx_buffer[valuepack_tx_index+3] = (i>>24)&0xff;

        valuepack_tx_index +=4;

        valuepack_stage = 4;
}
}
int fi;
void putFloat(float f)
{
if(valuepack_stage<=5)
{
                if(valuepack_tx_bit_index!=0)
        {        
                valuepack_tx_index++;
          valuepack_tx_bit_index = 0;
        }

        fi = *(int*)(&f);
        valuepack_tx_buffer[valuepack_tx_index] = fi&0xff;
        valuepack_tx_buffer[valuepack_tx_index+1] = (fi>>8)&0xff;
        valuepack_tx_buffer[valuepack_tx_index+2] = (fi>>16)&0xff;
        valuepack_tx_buffer[valuepack_tx_index+3] = (fi>>24)&0xff;
        valuepack_tx_index +=4;
        valuepack_stage = 5;
}
}


unsigned short endValuePack()
{
  unsigned char sum=0;
        for(int i=1;i<valuepack_tx_index;i++)
        {
                sum+=valuepack_tx_buffer;
        }
        valuepack_tx_buffer[valuepack_tx_index] = sum;
        valuepack_tx_buffer[valuepack_tx_index+1] = PACK_TAIL;
        return valuepack_tx_index+2;
}

将文中函数static void sp_gatt_write_cb(uint8_t *write_buf, uint16_t len, uint16_t att_idx,uint8_t conn_idx)更改为如下内容:

static void sp_gatt_write_cb(uint8_t *write_buf, uint16_t len, uint16_t att_idx,uint8_t conn_idx)
{
        for (int i = 0; i < len; i++)
    {
                co_printf("Write request: len: %d, 0x%x \r\n", len, write_buf);
                        //DI
                //#ifdef TYCON_DEBUG        
gatt_ntf_t ntf_att;
                        uint8_t ble_ntf_buff[30];
                        uint8_t ble_ntf_length;
                        int16_t ret=0;
                        int32_t temperature,humidity;
                        if(0x05== write_buf)
                {
                        ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
                        ntf_att.conidx=conn_idx;
                        ntf_att.svc_id=sp_svc_id;
                        ret = sht3x_measure_blocking_read(&temperature, &humidity);//Read temperature   humidity
                        startValuePack(ble_ntf_buff);
                        putFloat(temperature /1000.0);
                        putFloat(humidity /1000.0);
                        ble_ntf_length = endValuePack();
                        ntf_att.data_len = ble_ntf_length;
                        ntf_att.p_data = ble_ntf_buff;
                        gatt_notification(ntf_att);
                        co_printf("Ack data->Mobile\r\n");
                        #if 0
                        ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
                        ntf_att.conidx=conn_idx;
                        ntf_att.svc_id=sp_svc_id;
                        ntf_att.data_len =6;
                        uint8_t ntf_data[]="ACK OK";
                        ntf_att.p_data = ntf_data;
                        gatt_notification(ntf_att);
                        #endif
                        }
                if(0x02 == write_buf)//正转
                {
                pwm_start(PWM_CHANNEL_4);
                co_delay_100us(25);
                pwm_start(PWM_CHANNEL_5);
                co_delay_100us(25);
                pwm_start(PWM_CHANNEL_0);
                co_delay_100us(25);
                pwm_start(PWM_CHANNEL_1);
                co_delay_100us(25);
                }        
                if(0x03 == write_buf) //反转
                {
                pwm_start(PWM_CHANNEL_1);
                co_delay_100us(25);
                pwm_start(PWM_CHANNEL_0);
                co_delay_100us(25);
                pwm_start(PWM_CHANNEL_5);
                co_delay_100us(25);               
                pwm_start(PWM_CHANNEL_4);
                co_delay_100us(25);                                                
                }        
                if(0x04 == write_buf) //停止
                {
                pwm_stop(PWM_CHANNEL_4);
                pwm_stop(PWM_CHANNEL_5);               
                pwm_stop(PWM_CHANNEL_0);               
                pwm_stop(PWM_CHANNEL_1);               
                        }                                
          if (att_idx == SP_IDX_CHAR1_VALUE)
            memcpy(sp_char1_value, write_buf, len);
        if (att_idx == SP_IDX_CHAR3_VALUE)
            memcpy(sp_char3_value, write_buf, len);
        if (att_idx == SP_IDX_CHAR5_VALUE)
            memcpy(sp_char5_value, write_buf, len);
    }
        uint16_t uuid = BUILD_UINT16( simple_profile_att_table[att_idx].uuid.p_uuid[0], simple_profile_att_table[att_idx].uuid.p_uuid[1] );
        if (uuid == GATT_CLIENT_CHAR_CFG_UUID)
    {
                co_printf("Notification status changed\r\n");
        if (att_idx == SP_IDX_CHAR4_CFG)
        {
            sp_char4_ccc[0] = write_buf[0];
            sp_char4_ccc[1] = write_buf[1];
            co_printf("Char4 ccc: 0x%x 0x%x \r\n", sp_char4_ccc[0], sp_char4_ccc[1]);
        }
    }
}
综上,即为目前的软硬件设置,软件更改处未设置防抖等措施,后期有时间再更改优化吧。希望对感兴趣的朋友互相学习。