本实验为RT-Thread Nano 入门线上培训作业题目8:实现蓝牙串口通信功能。
当手机端蓝牙调试助手发送:”LED1 Toggle”时,实现LED1灯闪烁;
当手机端蓝牙调试助手发送:”LED2 Toggle”时,实现LED2灯闪烁;
当手机端蓝牙调试助手发送:”LEDR/G/B BrightnessN”(其中最后一个字母N表示亮度等级,可取为1,2,3,4,5,数字越大,越亮)时,调节LEDR/G/B的亮度等级,LEDR表示红灯,LEDG表示绿灯,LEDB表示蓝灯。
熟悉蓝牙串口的使用。
1、硬件环境:野火STM32霸道开发板,蓝牙串口。
2、软件环境:RT-Thread Nano 3.1.3,MDK 5.25
1、编写串口驱动,我这里使用串口1连接蓝牙,使用串口接收中断+消息队列的形式解析串口接收的数据。这部分的关键代码如下:
void USART1_IRQHandler(void)
{
u8 data;
if( USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET )
{
data = (uint8_t)USART_ReceiveData(USART1);
rt_mq_send(usart1_recv_msg_mq, (void*)&data, 1); /* 将接收到的数据发送线程 */
USART_ClearFlag(USART1, USART_FLAG_RXNE);
}
if( USART_GetFlagStatus(USART1, USART_FLAG_ORE) == SET )
{
USART_ClearFlag(USART1, USART_FLAG_ORE);
}
}
2、编写LED PWM驱动,这部分开发板固件库例程已经自带,不再赘述。
3、使用一个定时器,进行LED1、2的闪烁APP。
void led_timer_timeout(void *parameter)
{
if( *(u8 *)parameter == 1 )
LED_Toggle(GPIOF, GPIO_Pin_7);
else if( *(u8 *)parameter == 2 )
LED_Toggle(GPIOF, GPIO_Pin_8);
}
4、实现LEDRGB的亮度APP
TaskStruct TaskThreads[] = {
//{"iwdg_thread", iwdg_thread_entry, RT_NULL, 512, 15, 10},
{"led_thread", led_thread_entry, RT_NULL, 512, 15, 10},
{"usart1_recv_thread", usart1_recv_thread_entry, RT_NULL, 512, 2, 10},
//{"adc_detect_thread", adc_detect_thread_entry, RT_NULL, 512, 5, 10},
/*********************************************************/
//用户添加线程参数
//例如:{线程名字,线程入口函数,线程入口函数参数,线程栈大小,线程的优先级,线程时间片},
{" ", RT_NULL, RT_NULL, RT_NULL, RT_NULL, RT_NULL},
};
static u8 GetLevel(char *str)
{
char buf[24];
char i;
for( i='0'; i<='9'; i++ )
{
sprintf(buf, "%s%s", "Brightness", &i);
if( strstr(str, buf) )
return (i-'0');
}
return 0;
}
void usart1_recv_thread_entry(void *parameter)
{
uint8_t *buffer = RT_NULL;
uint8_t data;
uint16_t index = 0;
rt_err_t res;
u8 led_level;
rt_timer_t led_timer;
u8 led_num;
usart1_recv_msg_mq = rt_mq_create("usart1_recv_msg_mq", /* 创建消息队列用于串口1数据接收 */
1, /* 消息队列最大长度 byte */
256, /* 消息的数量 */
RT_IPC_FLAG_FIFO); /* 如果有多个线程等待此消息队列,采用先进先出的方式进行线程切换 */
if( usart1_recv_msg_mq != RT_NULL )
rt_kprintf("usart1_recv_msg_mq success!\n");
else
rt_kprintf("usart1_recv_msg_mq fail!\n");
led_timer = rt_timer_create("led_timer", /* 定时器名字 */
led_timer_timeout, /* 定时器回调函数 */
(void *)&led_num, /* 参数 */
1000, /* 1s检测一次按键 */
RT_TIMER_FLAG_PERIODIC | /* 周期调用 */
RT_TIMER_FLAG_SOFT_TIMER ); /* 软件定时 */
buffer = rt_malloc(256); /* 分配一个256字节的串口接收缓冲区 */
if( buffer == RT_NULL )
{
rt_kprintf("buffer fail!\n");
return ;
}
while(1)
{
res = rt_mq_recv(usart1_recv_msg_mq, (void*)&data, 1, 40); /* 从消息队列中接收消息到 data */
if( RT_EOK == res ) /* 有数据过来,将数据进行保存 */
{
index = index >= 254 ? 254 : index;
buffer[index++] = data;
}
else if( -RT_ETIMEOUT == res ) /* 超出40ms没有接收到数据,说明串口空闲了 */
{
if( index > 0 )
{
buffer[index] = '\0';
rt_kprintf("Usart1 Recv Buffer : %s \n", buffer);
if( strstr((char *)buffer, "Toggle") && strstr((char *)buffer, "LED1") )
{
rt_timer_stop(led_timer);
led_num = 1;
rt_timer_start(led_timer);
}
else if( strstr((char *)buffer, "Toggle") && strstr((char *)buffer, "LED2") )
{
rt_timer_stop(led_timer);
led_num = 2;
rt_timer_start(led_timer);
}
else
{
led_level = 0;
led_level = GetLevel((char *)buffer);
if( strstr((char *)buffer, "LEDR") )
{
SetColorValue(20*led_level, 0, 0);
}
else if( strstr((char *)buffer, "LEDG" ) )
{
SetColorValue(0, 20*led_level, 0);
}
else if( strstr((char *)buffer, "LEDB" ) )
{
SetColorValue(0, 0, 20*led_level);
}
}
rt_memset(buffer, 0, 256); /* 清空缓冲区 */
index = 0;
}
}
else if( -RT_ERROR == res )
{
rt_kprintf("usart1_recv_msg_mq res error!");
}
}
}