收到富芮坤的开发板已经一个多月了,以前玩的蓝牙用的是串口驱动的模块,用的时候只需要通过串口将数据丢给模块即可。第一次玩蓝牙芯片还是蛮有意思的,首先收到的是一个小盒子。
打开盒子能看到的是一块板子,一条数据线和一张资料下载的说明。如果你手上没有专业的下载器,不要慌,可以用提供的数据线下载。
光说不练假把式,上电椅额,,,找把椅子坐好上电。{:2_39:}首先看到的就是富芮坤的图标。至于上面有啥资源,用到再说。(说了你也记不住,记住了也用不到,不说也罢)
看完正面后给他翻个身,能看到显示屏的接口和富芮坤的logo。
接下来先看看它本身有啥功能吧,这里有的小伙伴说按键按下没反应,其实是需要先将板上的管脚进行短接。将KEY1和KEY2分别短接后按键就能用了,要使用到串口也同样需要分别短接TXD和RXD,后续要用到温湿度传感器的话还得将SDA和SCL分别短接。
下面介绍一下人机交互界面,左边的是复位按键,中间的是模式切换按键,右边的用于在当前模式下选择不同的内容。
下面看看串口的数据,开机上电后串口会打印出一串数据,至于是啥我也不关心。
但是发现个有趣的现象,长按按钮和短按按钮是能被识别到的
将模式切换到温湿度模式后(按中间的按钮切换),串口会对应显示温湿度,气压,步数等信息。当然显示屏上也会有对应的显示。
好了,开完箱看完官方的东西就开始魔改了。首先把初始化图片搞掉!!
要显示图片首先要将图片转为数据,这里可以使用“image2lcd”。安装完成打开后如图9所示。点击右下角的注册输入注册码即可完成注册,如不注册生成的图片会有水印。左下角的参数设置不要搞错。打开导入图片(应为bmp格式),点击保存后保存为.h文件即可。
生成数据后数据要放哪里才能显示为图片呢?这是个好问题!!!在你的工程里找bmp.h文件,把里面的数据替换掉就OK了。
编译下载后效果如图所示。(因为之前image2lcd没有注册,所以图左上角有水印;注册后如图显示,左上角的水印消失了。)
图片显示的代码如图14所示。图片的尺寸为240*240,长度len“480*240”为数组的大小115200。
说了半天,蓝牙开发板首要功能当然是和手机的蓝牙连接啦。
修改“simple_gatt_service.c” 中的“sp_gatt_write_cb”函数,在函数里面加一个定义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[i]);
- if(len==1)
- {
- gatt_ntf_t ntf_att;
- ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
- ntf_att.conidx=conn_idx;
- ntf_att.svc_id=sp_svc_id;
- uint8_t ShowStringBuff[30] = {0};
- sprintf((char *)ShowStringBuff,"Hello %s!",(char *)write_buf);
- ntf_att.p_data = (uint8_t *)ShowStringBuff;
- ntf_att.data_len =sizeof(ShowStringBuff);
- gatt_notification(ntf_att);
- //LCD_ShowString(10,75,ShowStringBuff,BLACK);
- co_printf("Write request: 111111 \r\n");
- }
- 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]);
- }
- }
- }
在调用函数时多传入一个参数p_msg->conn_idx。
- static uint16_t sp_gatt_msg_handler(gatt_msg_t *p_msg)
- {
- switch(p_msg->msg_evt)
- {
- case GATTC_MSG_READ_REQ:
- sp_gatt_read_cb((uint8_t *)(p_msg->param.msg.p_msg_data), &(p_msg->param.msg.msg_len), p_msg->att_idx);
- break;
- case GATTC_MSG_WRITE_REQ:
- sp_gatt_write_cb((uint8_t*)(p_msg->param.msg.p_msg_data), (p_msg->param.msg.msg_len), p_msg->att_idx,p_msg->conn_idx);
- break;
- default:
- break;
- }
- return p_msg->param.msg.msg_len;
- }
手机蓝牙调试器参数设置:
代码看不懂?问题不大,最后附上源码!!
先看看最后的效果,时间的计时使用的是定时器,时间的更新和PWM数值的接收都来自同一个函数sp_gatt_write_cb
talk is cheep,show me the code!
开发板接受手机端的数据
- extern uint16_t sec,min,hor;
- static void sp_gatt_write_cb(uint8_t *write_buf, uint16_t len, uint16_t att_idx,uint8_t conn_idx)
- {
- co_printf("len:%d\r\n",len);
- for (int i = 0; i < len; i++)
- {
- co_printf("Write request: len: %d, 0x%x \r\n", len, write_buf[i]);
- /*设置时间*/
- if(write_buf[0] == 0xa5)
- {
- if(write_buf[1] == 0x01)
- hor = write_buf[2];
- else if(write_buf[1] == 0x02)
- min = write_buf[2];
- else if(write_buf[1] == 0x04)
- sec = write_buf[2];
- }
- /*设置pwm*/
- pwm_update(PWM_CHANNEL_1,1000,write_buf[3]);
- /*发送数据到手机*/
- // if(len==1)
- // {
- // gatt_ntf_t ntf_att;
- // ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
- // ntf_att.conidx=conn_idx;
- // ntf_att.svc_id=sp_svc_id;
- // uint8_t ShowStringBuff[30] = {0};
- // sprintf((char *)ShowStringBuff,"Hello %s!",(char *)write_buf);
- // ntf_att.p_data = (uint8_t *)ShowStringBuff;
- // ntf_att.data_len =sizeof(ShowStringBuff);
- // gatt_notification(ntf_att);
- //
- // //LCD_ShowString(10,75,ShowStringBuff,BLACK);
- // co_printf("Write request: 111111 \r\n");
- // }
- 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]);
- }
- }
- }
周期为1s的定时器,这个函数负责时间的定时计数和定时向手机发送温湿度等数据。
- uint16_t sec = 0,min = 0,hor = 0;
- uint8_t ble_ntf_buff[30],ble_ntf_length;
- void timer_refresh_fun(void *arg)
- {
- int32_t temperature, humidity;
- uint8_t LCD_ShowStringBuff[30];
- float capb18_temp =0,capb_airpress=0;
- int8_t ret=0;
- /*时间*/
- sec++;
- if(sec>=60)
- {
- sec = 0;
- min++;
- }
- if(min>=60)
- {
- hor++;
- }
- if(hor>=24)
- {
- hor = 0;
- }
- co_printf("%d:%d:%d\r\n",hor,min,sec);
- switch (App_Mode)
- {
- case PICTURE_UPDATE:
- break;
- case SENSOR_DATA:
- //SHT30数据读取,并在lcd上显示
- ret = sht3x_measure_blocking_read(&temperature, &humidity);//Read temperature humidity
- if (ret == STATUS_OK)
- {
- co_printf("temperature = %d,humidity = %d\r\n",temperature,humidity);
- sprintf((char *)LCD_ShowStringBuff,"SHT30_T= %0.1f ",temperature/1000.0);
- LCD_ShowString(20,140,LCD_ShowStringBuff,BLACK);
- sprintf((char *)LCD_ShowStringBuff,"SHT30_H= %0.1f%% ",humidity/1000.0);
- LCD_ShowString(20,160,LCD_ShowStringBuff,BLACK);
- /*数据打包*/
- startValuePack(ble_ntf_buff);
- putFloat(temperature /1000.0);
- putFloat(humidity /1000.0);
- ble_ntf_length = endValuePack();
- /*数据发到手机*/
- gatt_ntf_t ntf_att;
- ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
- ntf_att.conidx=0;
- ntf_att.svc_id=73;
- ntf_att.p_data = ble_ntf_buff;
- ntf_att.data_len =ble_ntf_length;
- gatt_notification(ntf_att);
- }
- else
- {
- co_printf("SHT30 error reading measurement\n");
- sprintf((char *)LCD_ShowStringBuff,"SHT30_T= error ");
- LCD_ShowString(20,140,LCD_ShowStringBuff,BLACK);
- sprintf((char *)LCD_ShowStringBuff,"SHT30_H= error ");
- LCD_ShowString(20,160,LCD_ShowStringBuff,BLACK);
- }
- //CAPB18数据读取,并在lcd上显示
- ret = CAPB18_data_get(&capb18_temp,&capb_airpress);
- if(ret == true)
- {
- sprintf((char*)LCD_ShowStringBuff,"CAPB18_PRS= %7.5f ",capb_airpress);
- co_printf("%s\r\n",LCD_ShowStringBuff);
- LCD_ShowString(20,180,LCD_ShowStringBuff,BLACK);
- sprintf((char*)LCD_ShowStringBuff,"CAPB18_TMP= %7.5f ",capb18_temp);
- co_printf("%s\r\n",LCD_ShowStringBuff);
- LCD_ShowString(20,200,LCD_ShowStringBuff,BLACK);
- }
- else
- {
- co_printf("CAPB18 error reading measurement\n");
- sprintf((char*)LCD_ShowStringBuff,"CAPB18_PRS= error ");
- co_printf("%s\r\n",LCD_ShowStringBuff);
- LCD_ShowString(20,180,LCD_ShowStringBuff,BLACK);
- sprintf((char*)LCD_ShowStringBuff,"CAPB18_TMP= error ");
- co_printf("%s\r\n",LCD_ShowStringBuff);
- LCD_ShowString(20,200,LCD_ShowStringBuff,BLACK);
- }
- //g-sensor读取,并在lcd上显示
- co_printf("=skip count=%d\r\n",get_skip_num());//获取跳动次数
- sprintf((char*)LCD_ShowStringBuff,"*skip count* = %d",get_skip_num());
- co_printf("%s\r\n",LCD_ShowStringBuff);
- LCD_ShowString(20,120,LCD_ShowStringBuff,BLACK);
- /*显示时间*/
- sprintf((char *)LCD_ShowStringBuff,"Time %d:%d:%d ",hor,min,sec);
- LCD_ShowString(20,100,LCD_ShowStringBuff,BLACK);
- break;
- case SPEAKER_FROM_FLASH://播放flash中的音频demo
- sprintf((char*)LCD_ShowStringBuff,"Press the key K2 to start the audio");
- LCD_ShowString(20,100,LCD_ShowStringBuff,BLACK);
- if(!Flash_data_state)
- {
- //如果flash中没有有效的音频数据,则提示如下
- sprintf((char*)LCD_ShowStringBuff,"There is no audio data in flash!");
- LCD_ShowString(20,140,LCD_ShowStringBuff,BLACK);
- }
- else
- {
- LCD_Fill(0,140,240,170,BACK_COLOR);//
- }
- break;
- default:
- break;
- }
- }
关于计步功能,官方的历程把手甩断都某得反应,查了半天的资料终于发现了点端倪,main函数里有这么一段代码,把它将0改为1即可。
- const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) =
- {
- .firmware_version = 0x00000001,
- };
关于视频中的风扇调速使用的是PWM,在main函数中使能硬件。(最后两行就是PWM初始化)
- void user_entry_before_ble_init(void)
- {
- /* set system power supply in BUCK mode */
- pmu_set_sys_power_mode(PMU_SYS_POW_BUCK);
- pmu_enable_irq(PMU_ISR_BIT_ACOK
- | PMU_ISR_BIT_ACOFF
- | PMU_ISR_BIT_ONKEY_PO
- | PMU_ISR_BIT_OTP
- | PMU_ISR_BIT_LVD
- | PMU_ISR_BIT_BAT
- | PMU_ISR_BIT_ONKEY_HIGH);
- NVIC_EnableIRQ(PMU_IRQn);
- // Enable UART print.
- system_set_port_pull(GPIO_PA2, true);
- system_set_port_mux(GPIO_PORT_A, GPIO_BIT_2, PORTA2_FUNC_UART1_RXD);
- system_set_port_mux(GPIO_PORT_A, GPIO_BIT_3, PORTA3_FUNC_UART1_TXD);
- uart_init(UART1, BAUD_RATE_115200);
- if(__jump_table.system_option & SYSTEM_OPTION_ENABLE_HCI_MODE)
- {
- /* use PC4 and PC5 for HCI interface */
- system_set_port_pull(GPIO_PA4, true);
- system_set_port_mux(GPIO_PORT_A, GPIO_BIT_4, PORTA4_FUNC_UART0_RXD);
- system_set_port_mux(GPIO_PORT_A, GPIO_BIT_5, PORTA5_FUNC_UART0_TXD);
- }
- /* used for debug, reserve 3S for j-link once sleep is enabled. */
- if(__jump_table.system_option & SYSTEM_OPTION_SLEEP_ENABLE)
- {
- co_delay_100us(10000);
- co_delay_100us(10000);
- co_delay_100us(10000);
- }
- //PWM初始化
- system_set_port_mux(GPIO_PORT_D,GPIO_BIT_7, PORTD7_FUNC_PWM1);
- //system_set_port_mux(GPIO_PORT_D,GPIO_BIT_5, PORTD5_FUNC_PWM5);
- pwm_init(PWM_CHANNEL_1,1000,99);
- //pwm_init(PWM_CHANNEL_5,1000,1);
- }
软件的初始化写在simple_peripheral_init中,如代码所示开启PWM就写在了最后一行。
- void simple_peripheral_init(void)
- {
- // set local device name
- uint8_t local_name[] = "Simple Peripheral";
- gap_set_dev_name(local_name, sizeof(local_name));
- // Initialize security related settings.
- gap_security_param_t param =
- {
- .mitm = false,
- .ble_secure_conn = false,
- .io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT,
- .pair_init_mode = GAP_PAIRING_MODE_WAIT_FOR_REQ,
- .bond_auth = true,
- .password = 0,
- };
- gap_security_param_init(¶m);
- gap_set_cb_func(app_gap_evt_cb);
- gap_bond_manager_init(0x7D000, 0x7E000, 8, true);
- gap_bond_manager_delete_all();
- mac_addr_t addr;
- gap_address_get(&addr);
- co_printf("Local BDADDR: 0x%2X%2X%2X%2X%2X%2X\r\n", addr.addr[0], addr.addr[1], addr.addr[2], addr.addr[3], addr.addr[4], addr.addr[5]);
- // Adding services to database
- sp_gatt_add_service();
- speaker_gatt_add_service(); //创建Speaker profile,
- //按键初始化 PD6 PC5
- 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);
- demo_LCD_APP(); //显示屏
- demo_CAPB18_APP(); //气压计
- demo_SHT3x_APP(); //温湿度
- gyro_dev_init(); //加速度传感器
- //OS Timer
- os_timer_init(&timer_refresh,timer_refresh_fun,NULL);//创建一个周期性1s定时的系统定时器
- os_timer_start(&timer_refresh,1000,1);
- pwm_start(PWM_CHANNEL_1);
- }
可能眼尖的就发现第二个界面的“富芮坤”怎么变了?咋整的?打开lcd.c文件能看到有一个lcd_show_logo函数,LCD_ShowChinese函数就是现实中文的函数。前两个参数是要显示的位置,第三个参数为数据所在数组的位置,第四个参数为中文的大小(其实就是选择不同的数组),最后一个为字体颜色。
- void lcd_show_logo(const uint8_t* mode_str)
- {
- uint8_t LCD_ShowStringBuff[30] = {0};
- BACK_COLOR=WHITE;
- LCD_Clear(WHITE);
- LCD_ShowChinese(10+TITLE_OFFSET,20,6,32,BLUE); //谭
- LCD_ShowChinese(85+TITLE_OFFSET,20,7,32,BLUE); //伟
- LCD_ShowChinese(160+TITLE_OFFSET,20,8,32,BLUE); //志
- sprintf((char *)LCD_ShowStringBuff,"Mode:");
- LCD_ShowString(5,75,LCD_ShowStringBuff,BLACK);
- LCD_ShowString(50, 75, mode_str, BLACK);
- }
应该没有漏掉什么要点了,那么就附上源码吧。打开代码请进入以下路径FR801xH-SDK\examples\dev1.0\ble_simple_peripheral\keil(最近在学github,之后会丢过去的)
学习心得: