【富芮坤物联网开发板评测】通过BLE控制步进电机,手机回传显示信息
一、鸣谢:
滴水之恩当涌泉相报,首先感谢由富芮坤冠名,面包板社区主办的原创物联网开发板设计大赛提供的这次机会,感谢上海富芮坤微电子有限公司为本次活动提供了100块FR8016HA物联网开发板,同时感谢面包板社区以及各位大神们提供的学习平台。别问我为何看得远,因为我站在了巨人的肩上。
二、目标:
通过手机蓝牙控制步进电机的正反转和停止(本来想设计一个智能玩具车,因为配件不全仅开发其控制和驱动的关键技术),并将温度和湿度回传手机显示。
开发的视频如下:
https://v.qq.com/x/page/l3080suxse6.html?vuid24=PLFtlYEoBNHSG83kEXjb2Q%3D%3D&url_from=share&second_share=0&share_from=copy
三、开发板初次见面:
开发板靓照,打开包装
正反面的靓照:
通电检查:
四、工程开发
1)搜集开发板的设计资料:
资料下载地址1:https://mbb.eet-china.com/download/203375.html
资料下载地址2:http://www.freqchip.com/gjhkfb
SDK下载:https://www.freqchip.com/sdkxz
本人的开发板是1.3版本所以重点关注(开发板资料---硬件版本v1.3,freqchip-FR801xH-SDK-master的SDK文件)。消化硬件和烧写程序的资料,因为KEIL比较常用,所以这里就不再啰嗦了,直接进入正题。
上电初次体验:首先安装USB驱动,CP210x_Windows_Drivers_with_Serial_Enumeration
使用freqchip-FR801xH-SDK-master\FR801xH-SDK\tools\FR8010H_Download_Tool.exe烧录SDK文件
2)硬件更改
学习FR8016HA 开发板使用手册,和FR8016HA_Dev_V1.3开发板原理图.pdf按照下图I/O外界跳线,进行跳线短接,此处K2需要短接到PA1,其余9处用短接帽短接。
步进电机:28BYJ-48 四相五线步进电机
驱动方式如下图,通过示波器观察PD4与PD6波形,在此注意点PWM的周期不能太短,不然容易失步,我在此就犯了一个低级错误。
3)手机APP设置
手机APP下载搜索”蓝牙调试器”安装,打开允许蓝牙接通,设备连接找到自己的开发板,名称中Simple Per的那个,点击加号等待连接。点击专业调试,点击加号创建工程-智能玩具车,通信设置发送数据和接收数据如下图所示,编辑控件添加文本框链接到温湿度,添加按钮链接到正转反转和停止如下图所示。为了方便我将正传设置为2,反转设置为3,停止设置为4,获取环境参数设置为5。发送用的是字节,接收用的是float。
4) 软件更改:
此部分为大头工作,一步错可能要走很多的弯路,所以我尽量把所有改动的代码贴出来。
因为步进电机要使用PD4、PD5、PD6、PD7,首先要将按键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]);
}
}
}
综上,即为目前的软硬件设置,软件更改处未设置防抖等措施,后期有时间再更改优化吧。希望对感兴趣的朋友互相学习。