本帖最后由 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
  1. static void sp_gatt_write_cb(uint8_t *write_buf, uint16_t len, uint16_t att_idx,uint8_t conn_idx)
  2. {
  3.         for (int i = 0; i < len; i++)
  4.     {
  5.                 co_printf("Write request: len: %d, 0x%x \r\n", len, write_buf[i]);
  6.         if(len==1)
  7.          {
  8.                                 gatt_ntf_t ntf_att;
  9.                                 ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
  10.                                 ntf_att.conidx=conn_idx;
  11.                                 ntf_att.svc_id=sp_svc_id;
  12.                                 uint8_t ShowStringBuff[30] = {0};
  13.                                 sprintf((char *)ShowStringBuff,"Hello %s!",(char *)write_buf);
  14.             ntf_att.p_data = (uint8_t *)ShowStringBuff;
  15.                                 ntf_att.data_len =sizeof(ShowStringBuff);
  16.             gatt_notification(ntf_att);               
  17.                                                 
  18.                                 //LCD_ShowString(10,75,ShowStringBuff,BLACK);
  19.                                 co_printf("Write request: 111111 \r\n");
  20.                 }
  21.       
  22.         if (att_idx == SP_IDX_CHAR1_VALUE)
  23.             memcpy(sp_char1_value, write_buf, len);
  24.         
  25.         if (att_idx == SP_IDX_CHAR3_VALUE)
  26.             memcpy(sp_char3_value, write_buf, len);
  27.         
  28.         if (att_idx == SP_IDX_CHAR5_VALUE)
  29.             memcpy(sp_char5_value, write_buf, len);
  30.     }
  31.         
  32.         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] );
  33.         if (uuid == GATT_CLIENT_CHAR_CFG_UUID)
  34.     {
  35.                 co_printf("Notification status changed\r\n");
  36.         if (att_idx == SP_IDX_CHAR4_CFG)
  37.         {
  38.             sp_char4_ccc[0] = write_buf[0];
  39.             sp_char4_ccc[1] = write_buf[1];
  40.             co_printf("Char4 ccc: 0x%x 0x%x \r\n", sp_char4_ccc[0], sp_char4_ccc[1]);
  41.         }
  42.     }
  43. }
   
    在调用函数时多传入一个参数p_msg->conn_idx。
  1. static uint16_t sp_gatt_msg_handler(gatt_msg_t *p_msg)
  2. {
  3.     switch(p_msg->msg_evt)
  4.     {
  5.         case GATTC_MSG_READ_REQ:
  6.             sp_gatt_read_cb((uint8_t *)(p_msg->param.msg.p_msg_data), &(p_msg->param.msg.msg_len), p_msg->att_idx);
  7.             break;
  8.         
  9.         case GATTC_MSG_WRITE_REQ:
  10.             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);
  11.             break;
  12.             
  13.         default:
  14.             break;
  15.     }
  16.     return p_msg->param.msg.msg_len;
  17. }
   
    手机蓝牙调试器参数设置:
1.jpg

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

    talk is cheep,show me the code!
    开发板接受手机端的数据
  1. extern uint16_t sec,min,hor;
  2. static void sp_gatt_write_cb(uint8_t *write_buf, uint16_t len, uint16_t att_idx,uint8_t conn_idx)
  3. {
  4.    co_printf("len:%d\r\n",len);
  5.         for (int i = 0; i < len; i++)
  6.     {
  7.        co_printf("Write request: len: %d, 0x%x \r\n", len, write_buf[i]);
  8.        /*设置时间*/
  9.        if(write_buf[0] == 0xa5)
  10.        {
  11.            if(write_buf[1] == 0x01)
  12.                hor = write_buf[2];
  13.            else if(write_buf[1] == 0x02)
  14.                min = write_buf[2];
  15.            else if(write_buf[1] == 0x04)
  16.                sec = write_buf[2];
  17.        }
  18.        /*设置pwm*/
  19.        pwm_update(PWM_CHANNEL_1,1000,write_buf[3]);
  20.        /*发送数据到手机*/
  21. //        if(len==1)
  22. //         {
  23. //                                gatt_ntf_t ntf_att;
  24. //                                ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
  25. //                                ntf_att.conidx=conn_idx;
  26. //                                ntf_att.svc_id=sp_svc_id;
  27. //                                uint8_t ShowStringBuff[30] = {0};
  28. //                                sprintf((char *)ShowStringBuff,"Hello %s!",(char *)write_buf);
  29. //            ntf_att.p_data = (uint8_t *)ShowStringBuff;
  30. //                                ntf_att.data_len =sizeof(ShowStringBuff);
  31. //            gatt_notification(ntf_att);               
  32. //                                                
  33. //                                //LCD_ShowString(10,75,ShowStringBuff,BLACK);
  34. //                                co_printf("Write request: 111111 \r\n");
  35. //                        }
  36.       
  37.         if (att_idx == SP_IDX_CHAR1_VALUE)
  38.             memcpy(sp_char1_value, write_buf, len);
  39.         
  40.         if (att_idx == SP_IDX_CHAR3_VALUE)
  41.             memcpy(sp_char3_value, write_buf, len);
  42.         
  43.         if (att_idx == SP_IDX_CHAR5_VALUE)
  44.             memcpy(sp_char5_value, write_buf, len);
  45.     }
  46.         
  47.         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] );
  48.         if (uuid == GATT_CLIENT_CHAR_CFG_UUID)
  49.     {
  50.                 co_printf("Notification status changed\r\n");
  51.         if (att_idx == SP_IDX_CHAR4_CFG)
  52.         {
  53.             sp_char4_ccc[0] = write_buf[0];
  54.             sp_char4_ccc[1] = write_buf[1];
  55.             co_printf("Char4 ccc: 0x%x 0x%x \r\n", sp_char4_ccc[0], sp_char4_ccc[1]);
  56.         }
  57.     }
  58. }
   
    周期为1s的定时器,这个函数负责时间的定时计数和定时向手机发送温湿度等数据。
  1. uint16_t sec = 0,min = 0,hor = 0;
  2. uint8_t ble_ntf_buff[30],ble_ntf_length;
  3. void timer_refresh_fun(void *arg)
  4. {
  5.         int32_t temperature, humidity;
  6.         uint8_t LCD_ShowStringBuff[30];
  7.         float capb18_temp =0,capb_airpress=0;
  8.         int8_t ret=0;
  9.    /*时间*/
  10.    sec++;
  11.    if(sec>=60)
  12.    {
  13.       sec = 0;
  14.       min++;
  15.    }
  16.    if(min>=60)
  17.    {
  18.       hor++;
  19.    }
  20.    if(hor>=24)
  21.    {
  22.       hor = 0;
  23.    }
  24.    co_printf("%d:%d:%d\r\n",hor,min,sec);
  25.         switch (App_Mode)
  26.         {
  27.         case PICTURE_UPDATE:
  28.             break;
  29.         case SENSOR_DATA:
  30.             //SHT30数据读取,并在lcd上显示
  31.             ret = sht3x_measure_blocking_read(&temperature, &humidity);//Read temperature   humidity
  32.             if (ret == STATUS_OK)
  33.             {
  34.                 co_printf("temperature = %d,humidity = %d\r\n",temperature,humidity);
  35.                 sprintf((char *)LCD_ShowStringBuff,"SHT30_T= %0.1f     ",temperature/1000.0);
  36.                 LCD_ShowString(20,140,LCD_ShowStringBuff,BLACK);
  37.                 sprintf((char *)LCD_ShowStringBuff,"SHT30_H= %0.1f%%   ",humidity/1000.0);
  38.                 LCD_ShowString(20,160,LCD_ShowStringBuff,BLACK);
  39.                
  40.                /*数据打包*/
  41.                startValuePack(ble_ntf_buff);
  42.                putFloat(temperature /1000.0);
  43.                putFloat(humidity /1000.0);
  44.                ble_ntf_length = endValuePack();
  45.                 /*数据发到手机*/
  46.                gatt_ntf_t ntf_att;
  47.                ntf_att.att_idx=SP_IDX_CHAR4_VALUE;
  48.                ntf_att.conidx=0;
  49.                ntf_att.svc_id=73;
  50.                
  51.                ntf_att.p_data = ble_ntf_buff;
  52.                ntf_att.data_len =ble_ntf_length;
  53.                gatt_notification(ntf_att);        
  54.             }
  55.             else
  56.             {
  57.                 co_printf("SHT30 error reading measurement\n");
  58.                
  59.                 sprintf((char *)LCD_ShowStringBuff,"SHT30_T= error    ");
  60.                 LCD_ShowString(20,140,LCD_ShowStringBuff,BLACK);
  61.                 sprintf((char *)LCD_ShowStringBuff,"SHT30_H= error    ");
  62.                 LCD_ShowString(20,160,LCD_ShowStringBuff,BLACK);
  63.             }
  64.             //CAPB18数据读取,并在lcd上显示
  65.             ret = CAPB18_data_get(&capb18_temp,&capb_airpress);
  66.             if(ret == true)
  67.             {
  68.                 sprintf((char*)LCD_ShowStringBuff,"CAPB18_PRS= %7.5f  ",capb_airpress);
  69.                 co_printf("%s\r\n",LCD_ShowStringBuff);
  70.                 LCD_ShowString(20,180,LCD_ShowStringBuff,BLACK);
  71.                 sprintf((char*)LCD_ShowStringBuff,"CAPB18_TMP= %7.5f  ",capb18_temp);
  72.                 co_printf("%s\r\n",LCD_ShowStringBuff);
  73.                 LCD_ShowString(20,200,LCD_ShowStringBuff,BLACK);
  74.             }
  75.             else
  76.             {
  77.                 co_printf("CAPB18 error reading measurement\n");
  78.                 sprintf((char*)LCD_ShowStringBuff,"CAPB18_PRS= error         ");
  79.                 co_printf("%s\r\n",LCD_ShowStringBuff);
  80.                 LCD_ShowString(20,180,LCD_ShowStringBuff,BLACK);
  81.                 sprintf((char*)LCD_ShowStringBuff,"CAPB18_TMP= error         ");
  82.                 co_printf("%s\r\n",LCD_ShowStringBuff);
  83.                 LCD_ShowString(20,200,LCD_ShowStringBuff,BLACK);
  84.             }
  85.                
  86.             //g-sensor读取,并在lcd上显示
  87.             co_printf("=skip count=%d\r\n",get_skip_num());//获取跳动次数
  88.             sprintf((char*)LCD_ShowStringBuff,"*skip count* = %d",get_skip_num());
  89.             co_printf("%s\r\n",LCD_ShowStringBuff);
  90.             LCD_ShowString(20,120,LCD_ShowStringBuff,BLACK);
  91.             /*显示时间*/
  92.             sprintf((char *)LCD_ShowStringBuff,"Time %d:%d:%d   ",hor,min,sec);
  93.             LCD_ShowString(20,100,LCD_ShowStringBuff,BLACK);
  94.             break;
  95.             
  96.         case SPEAKER_FROM_FLASH://播放flash中的音频demo
  97.             sprintf((char*)LCD_ShowStringBuff,"Press the key K2 to start the audio");
  98.             LCD_ShowString(20,100,LCD_ShowStringBuff,BLACK);
  99.             if(!Flash_data_state)
  100.             {
  101.                 //如果flash中没有有效的音频数据,则提示如下
  102.                 sprintf((char*)LCD_ShowStringBuff,"There is no audio data in flash!");
  103.                 LCD_ShowString(20,140,LCD_ShowStringBuff,BLACK);
  104.             }
  105.             else
  106.             {
  107.                 LCD_Fill(0,140,240,170,BACK_COLOR);//
  108.             }
  109.             break;
  110.             
  111.         default:
  112.             break;
  113.                
  114.         }
  115. }
   
    关于计步功能,官方的历程把手甩断都某得反应,查了半天的资料终于发现了点端倪,main函数里有这么一段代码,把它将0改为1即可。
  1. const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) =
  2. {
  3.     .firmware_version = 0x00000001,
  4. };
   
     关于视频中的风扇调速使用的是PWM,在main函数中使能硬件。(最后两行就是PWM初始化)
  1. void user_entry_before_ble_init(void)
  2. {   
  3.     /* set system power supply in BUCK mode */
  4.     pmu_set_sys_power_mode(PMU_SYS_POW_BUCK);
  5.     pmu_enable_irq(PMU_ISR_BIT_ACOK
  6.                    | PMU_ISR_BIT_ACOFF
  7.                    | PMU_ISR_BIT_ONKEY_PO
  8.                    | PMU_ISR_BIT_OTP
  9.                    | PMU_ISR_BIT_LVD
  10.                    | PMU_ISR_BIT_BAT
  11.                    | PMU_ISR_BIT_ONKEY_HIGH);
  12.     NVIC_EnableIRQ(PMU_IRQn);
  13.    
  14.     // Enable UART print.
  15.     system_set_port_pull(GPIO_PA2, true);
  16.     system_set_port_mux(GPIO_PORT_A, GPIO_BIT_2, PORTA2_FUNC_UART1_RXD);
  17.     system_set_port_mux(GPIO_PORT_A, GPIO_BIT_3, PORTA3_FUNC_UART1_TXD);
  18.     uart_init(UART1, BAUD_RATE_115200);   
  19.     if(__jump_table.system_option & SYSTEM_OPTION_ENABLE_HCI_MODE)
  20.     {
  21.         /* use PC4 and PC5 for HCI interface */
  22.         system_set_port_pull(GPIO_PA4, true);
  23.         system_set_port_mux(GPIO_PORT_A, GPIO_BIT_4, PORTA4_FUNC_UART0_RXD);
  24.         system_set_port_mux(GPIO_PORT_A, GPIO_BIT_5, PORTA5_FUNC_UART0_TXD);
  25.     }
  26.     /* used for debug, reserve 3S for j-link once sleep is enabled. */
  27.     if(__jump_table.system_option & SYSTEM_OPTION_SLEEP_ENABLE)
  28.     {
  29.         co_delay_100us(10000);
  30.         co_delay_100us(10000);
  31.         co_delay_100us(10000);
  32.     }
  33.     //PWM初始化
  34.    system_set_port_mux(GPIO_PORT_D,GPIO_BIT_7, PORTD7_FUNC_PWM1);
  35.    //system_set_port_mux(GPIO_PORT_D,GPIO_BIT_5, PORTD5_FUNC_PWM5);
  36.    pwm_init(PWM_CHANNEL_1,1000,99);
  37.    //pwm_init(PWM_CHANNEL_5,1000,1);
  38. }
   
    软件的初始化写在simple_peripheral_init中,如代码所示开启PWM就写在了最后一行。
  1. void simple_peripheral_init(void)
  2. {
  3.     // set local device name
  4.         uint8_t local_name[] = "Simple Peripheral";
  5.         gap_set_dev_name(local_name, sizeof(local_name));
  6.         // Initialize security related settings.
  7.         gap_security_param_t param =
  8.         {
  9.             .mitm = false,
  10.             .ble_secure_conn = false,
  11.             .io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT,
  12.             .pair_init_mode = GAP_PAIRING_MODE_WAIT_FOR_REQ,
  13.             .bond_auth = true,
  14.             .password = 0,
  15.         };
  16.         gap_security_param_init(¶m);
  17.         gap_set_cb_func(app_gap_evt_cb);
  18.         gap_bond_manager_init(0x7D000, 0x7E000, 8, true);
  19.         gap_bond_manager_delete_all();
  20.         mac_addr_t addr;
  21.         gap_address_get(&addr);
  22.         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]);
  23.         // Adding services to database
  24.     sp_gatt_add_service();
  25.         speaker_gatt_add_service();                                    //创建Speaker profile,
  26.    
  27.         //按键初始化 PD6 PC5
  28.         pmu_set_pin_pull(GPIO_PORT_D, (1<<GPIO_BIT_6), true);
  29.         pmu_set_pin_pull(GPIO_PORT_C, (1<<GPIO_BIT_5), true);
  30.         pmu_port_wakeup_func_set(GPIO_PD6|GPIO_PC5);
  31.         button_init(GPIO_PD6|GPIO_PC5);
  32.         demo_LCD_APP();                                                                    //显示屏
  33.         demo_CAPB18_APP();                                                            //气压计
  34.         demo_SHT3x_APP();                                                            //温湿度
  35.         gyro_dev_init();                                                            //加速度传感器
  36.         
  37.         //OS Timer
  38.         os_timer_init(&timer_refresh,timer_refresh_fun,NULL);//创建一个周期性1s定时的系统定时器
  39.         os_timer_start(&timer_refresh,1000,1);
  40.    pwm_start(PWM_CHANNEL_1);
  41. }
   
    可能眼尖的就发现第二个界面的“富芮坤”怎么变了?咋整的?打开lcd.c文件能看到有一个lcd_show_logo函数,LCD_ShowChinese函数就是现实中文的函数。前两个参数是要显示的位置,第三个参数为数据所在数组的位置,第四个参数为中文的大小(其实就是选择不同的数组),最后一个为字体颜色。
  1. void lcd_show_logo(const uint8_t*  mode_str)
  2. {
  3.         uint8_t LCD_ShowStringBuff[30] = {0};
  4.         BACK_COLOR=WHITE;
  5.         LCD_Clear(WHITE);
  6.         LCD_ShowChinese(10+TITLE_OFFSET,20,6,32,BLUE);   //谭
  7.         LCD_ShowChinese(85+TITLE_OFFSET,20,7,32,BLUE);   //伟
  8.         LCD_ShowChinese(160+TITLE_OFFSET,20,8,32,BLUE);   //志
  9.         sprintf((char *)LCD_ShowStringBuff,"Mode:");
  10.         LCD_ShowString(5,75,LCD_ShowStringBuff,BLACK);
  11.         LCD_ShowString(50, 75, mode_str, BLACK);
  12.         
  13. }
    应该没有漏掉什么要点了,那么就附上源码吧。打开代码请进入以下路径FR801xH-SDK\examples\dev1.0\ble_simple_peripheral\keil(最近在学github,之后会丢过去的)

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