本帖最后由 Z_Tam 于 2020-7-28 20:30 编辑

    收到富芮坤的开发板已经一个多月了,以前玩的蓝牙用的是串口驱动的模块,用的时候只需要通过串口将数据丢给模块即可。第一次玩蓝牙芯片还是蛮有意思的,首先收到的是一个小盒子。
37a3008e9244bbe09cca3a4431cff6a.jpg

    打开盒子能看到的是一块板子,一条数据线和一张资料下载的说明。如果你手上没有专业的下载器,不要慌,可以用提供的数据线下载。
ac6b85f69e6a862cba055f874fdab28.jpg

    光说不练假把式,上电椅额,,,找把椅子坐好上电。{:2_39:}首先看到的就是富芮坤的图标。至于上面有啥资源,用到再说。(说了你也记不住,记住了也用不到,不说也罢)
3.jpg

    看完正面后给他翻个身,能看到显示屏的接口和富芮坤的logo。
4.jpg

    接下来先看看它本身有啥功能吧,这里有的小伙伴说按键按下没反应,其实是需要先将板上的管脚进行短接。将KEY1和KEY2分别短接后按键就能用了,要使用到串口也同样需要分别短接TXD和RXD,后续要用到温湿度传感器的话还得将SDA和SCL分别短接。
5iic.jpg

    下面介绍一下人机交互界面,左边的是复位按键,中间的是模式切换按键,右边的用于在当前模式下选择不同的内容。
6.jpg

    下面看看串口的数据,开机上电后串口会打印出一串数据,至于是啥我也不关心。
9开机.PNG

    但是发现个有趣的现象,长按按钮和短按按钮是能被识别到的
10按键.PNG

    将模式切换到温湿度模式后(按中间的按钮切换),串口会对应显示温湿度,气压,步数等信息。当然显示屏上也会有对应的显示。
11温湿度.PNG
7.jpg

    好了,开完箱看完官方的东西就开始魔改了。首先把初始化图片搞掉!
    要显示图片首先要将图片转为数据,这里可以使用“image2lcd”。安装完成打开后如图9所示。点击右下角的注册输入注册码即可完成注册,如不注册生成的图片会有水印。左下角的参数设置不要搞错。打开导入图片(应为bmp格式),点击保存后保存为.h文件即可。
1.png

    生成数据后数据要放哪里才能显示为图片呢?这是个好问题!!!在你的工程里找bmp.h文件,把里面的数据替换掉就OK了。
1.png

1.png

    编译下载后效果如图所示。(因为之前image2lcd没有注册,所以图左上角有水印;注册后如图显示,左上角的水印消失了。)
1.jpg
2.jpg

   图片显示的代码如图14所示。图片的尺寸为240*240,长度len“480*240”为数组的大小115200
1.png

    说了半天,蓝牙开发板首要功能当然是和手机的蓝牙连接啦。
    修改“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;
  • }
  • 复制代码
       
        手机蓝牙调试器参数设置:
    1.jpg

        代码看不懂?问题不大,最后附上源码!!
        先看看最后的效果,时间的计时使用的是定时器,时间的更新和PWM数值的接收都来自同一个函数sp_gatt_write_cb
        7222a54741c9894b1ad10517d107fc5.jpg
    4b864a3194d3b2d0f8fa941ae1ca9e1.jpg

        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,之后会丢过去的)

        学习心得: 心得.pdf (1.06 MB, 下载次数: 19)