@
目錄
前言
開發板:正點原子 STM32F103 精英版
語言:C語言
開發環境:Keil5
使用了 KEY LED USART USB轉TTL模塊 智向的藍牙模塊(ps:電腦安裝驅動CH340)
代碼下載:碼雲 GitHub
代碼參考:正點原子 源碼 串口實驗例程
功能介紹:
1、LED的0.2秒一閃,表示程序正在運行。
2、串口1收到的數據會發給串口3,串口3收到的數據會發給串口1。
3、按鍵KEY1按下會向串口1發送數據‘1’,按鍵KEY0按下會向串口3發送數據‘3’。
接線
USB轉TTL
藍牙
效果圖
USB轉TTL
藍牙
使用的手機軟件(安卓)為 BLE調試助手
打開軟件、藍牙、給予權限等
掃描到我們的藍牙模塊,然后連接
連接成功后
點擊最下面的 Unkonwn Service,展開,有接收 和 發送 按鈕
手機收 電腦發
手機發 電腦收
藍牙的連接/斷開
藍牙收到了手機發來的 連接 和 斷開 信息
參考用圖
STM32F103
藍牙模塊相關
核心代碼
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart1.h"
#include "usart3.h"
// 串口收發函數 type為1,串口1收,發往串口3 type不為1,串口3,發往串口1
void usart_recv_send(u8 type);
int main(void)
{
vu8 key = 0;
delay_init(); // 延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 設置NVIC中斷分組2:2位搶占優先級,2位響應優先級
usart1_init(115200); // USART1初始化 波特率115200 默認數據位8 停止位1 校驗位none
usart3_init(115200); // USART3初始化 波特率115200 默認數據位8 停止位1 校驗位none
LED_Init(); // LED端口初始化
KEY_Init(); // 初始化與按鍵連接的硬件接口
while (1)
{
// 串口收發
usart_recv_send(1);
usart_recv_send(3);
// 得到鍵值
key = KEY_Scan(0);
if (key)
{
switch (key)
{
case KEY1_PRES: // 向串口1發送'1'
usart1_send_byte(0x31);
break;
case KEY0_PRES: // 向串口3發送'3'
usart3_send_byte(0x33);
break;
}
}
LED0 = !LED0; //閃爍LED,提示系統正在運行.
delay_ms(100);
}
}
// 串口收發函數 type為1,串口1收,發往串口3 type不為1,串口3,發往串口1
void usart_recv_send(u8 type)
{
u8 i = 0;
u8 tmp_len = 0;
// 數據緩存
static u8 buf[255] = {0};
// 數據長度
u8 buf_len = 0;
// 返回緩存區數據的個數
if(1 == type)
tmp_len = usart1_getdata_count();
else
tmp_len = usart3_getdata_count();
for(i=0; i<tmp_len; i++)
{
// 返回緩存區當前指針所指數據
if(1 == type)
buf[i] = usart1_receive_data();
else
buf[i] = usart3_receive_data();
buf_len++;
// 超過約定的上限長度
if(buf_len >= 250)
{
buf_len=0;
break;
}
}
// 數據不為空
if(0 != buf_len)
{
// 串口數據發送
if(1 == type)
usart3_send_bytes(buf, buf_len);
else
usart1_send_bytes(buf, buf_len);
}
}
usart1.c
#include "usart1.h"
#include "stdio.h"
static uint8_t usart1_buffer[255];
static uint8_t usart1_index;
static uint8_t usart1_count;
// USART1初始化 默認數據位8 停止位1 校驗位none
void usart1_init(u32 bound)
{
// GPIO端口設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1,GPIOA時鍾
/* USART1 GPIO config */
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.9
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10
// Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 無硬件數據流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收發模式
USART_Init(USART1, &USART_InitStructure); // 初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 開啟串口接受中斷
USART_Cmd(USART1, ENABLE); // 使能串口1
usart1_index=0;
usart1_count=0;
}
// 中斷服務函數
void USART1_IRQHandler(void)
{
// 接收中斷
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
if(usart1_count==usart1_index)
{
// 緩存區無數據,回到起點開始存儲
usart1_count=0;
usart1_index=0;
}
usart1_buffer[usart1_count]=USART1->DR;
usart1_count++;
if(usart1_count>=250)
{
// 沒有及時取出數據,導致存儲位置到達末尾,回到起點
usart1_count=0;
usart1_index=0;
}
}
}
/*返回緩存區數據的個數*/
uint8_t usart1_getdata_count(void)
{
return usart1_count-usart1_index;
}
/*返回緩存區當前指針所指數據*/
uint8_t usart1_receive_data(void)
{
return usart1_buffer[usart1_index++];
}
/*串口數據發送函數
data_send:發送數據
*/
void usart1_send_byte(uint8_t data_send)
{
USART_SendData(USART1, data_send);
while (!(USART1->SR & USART_FLAG_TXE));
}
/*串口數據發送函數
data_buffer:發送數據串的首地址
length:發送數據的長度
*/
void usart1_send_bytes(uint8_t* data_buffer,uint8_t length)
{
uint8_t i;
for(i=0; i<length; i++)
{
usart1_send_byte(data_buffer[i]);
}
}
usart3.c
#include "usart3.h"
#include "stdio.h"
static uint8_t usart3_buffer[255];
static uint8_t usart3_index;
static uint8_t usart3_count;
// USART3初始化 默認 數據位8 停止位1 校驗位none
void usart3_init(u32 bound)
{
// GPIO端口設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* config USART3 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能USART3,GPIOB時鍾
/* USART3 GPIO config */
/* Configure USART3 Tx (PB.10) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure USART3 Rx (PB.11) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 無硬件數據流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收發模式
USART_Init(USART3, &USART_InitStructure); // 初始化串口3
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); // 開啟串口接受中斷
USART_Cmd(USART3, ENABLE); // 使能串口3
usart3_index=0;
usart3_count=0;
}
// 中斷服務函數
void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
if(usart3_count==usart3_index)
{
//緩存區無數據,回到起點開始存儲
usart3_count=0;
usart3_index=0;
}
usart3_buffer[usart3_count]=USART3->DR;
usart3_count++;
if(usart3_count>=250)
{
//沒有及時取出數據,導致存儲位置到達末尾,回到起點
usart3_count=0;
usart3_index=0;
}
}
}
/*返回緩存區數據的個數*/
uint8_t usart3_getdata_count(void)
{
return usart3_count-usart3_index;
}
/*返回緩存區當前指針所指數據*/
uint8_t usart3_receive_data(void)
{
return usart3_buffer[usart3_index++];
}
/*串口數據發送函數
data_send:發送數據
*/
void usart3_send_byte(uint8_t data_send)
{
USART_SendData(USART3, data_send);
while (!(USART3->SR & USART_FLAG_TXE));
}
/*串口數據發送函數
data_buffer:發送數據串的首地址
length:發送數據的長度
*/
void usart3_send_bytes(uint8_t* data_buffer,uint8_t length)
{
uint8_t i;
for(i=0; i<length; i++)
{
usart3_send_byte(data_buffer[i]);
}
}
拓展應用
實現簡單的賬號認證,通過命令控制LED1和蜂鳴器的開關
功能介紹
手機連接藍牙,發送登錄命令login#admin#admin
login#用戶名#密碼#
登錄成功后,發送控制命令
cmd#device#status#
最后發送登出命令cmd#login#out
效果圖
代碼
main.c
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart1.h"
#include "usart3.h"
#include "string.h"
#include "stdio.h"
#include "beep.h"
u8 login = 0;
u8 username[21] = "admin";
u8 password[21] = "admin";
// 串口收發函數 type為1,串口1收,發往串口3 type不為1,串口3收,發往串口1
void usart_recv_send(u8 type);
// 檢查登錄 傳入收到的數據和數據長度 返回 0驗證成功,1賬號或密碼錯誤,2數據超長
u8 check_login(u8* buf, u8 len);
/*
函數功能: 命令解析
傳參: 收到的數據和數據長度
命令格式: cmd#device#status#
返回: 0不符合規則 1解析成功
*/
u8 cmd_analysis(u8* buf, u8 len);
int main(void)
{
vu8 key = 0;
delay_init(); // 延時函數初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 設置NVIC中斷分組2:2位搶占優先級,2位響應優先級
usart1_init(115200); // USART1初始化 波特率115200 默認數據位8 停止位1 校驗位none
usart3_init(115200); // USART3初始化 波特率115200 默認數據位8 停止位1 校驗位none
LED_Init(); // LED端口初始化
KEY_Init(); // 初始化與按鍵連接的硬件接口
BEEP_Init(); // 蜂鳴器初始化
LED0 = 1;
while (1)
{
// 串口收發
usart_recv_send(1);
usart_recv_send(3);
// 得到鍵值
key = KEY_Scan(0);
if (key)
{
switch (key)
{
case KEY1_PRES: // 向串口1發送'1'
usart1_send_byte(0x31);
break;
case KEY0_PRES: // 向串口3發送'3'
usart3_send_byte(0x33);
break;
}
}
LED0 = !LED0; //閃爍LED,提示系統正在運行.
delay_ms(100);
}
}
// 串口收發函數 type為1,串口1收,發往串口3 type不為1,串口3收,發往串口1
void usart_recv_send(u8 type)
{
u8 i = 0;
u8 tmp_len = 0;
// 數據緩存
static u8 buf[255] = {0};
//static u8 buf2[255] = {0};
// 數據長度
u8 buf_len = 0;
// 返回緩存區數據的個數
if(1 == type)
tmp_len = usart1_getdata_count();
else
tmp_len = usart3_getdata_count();
for(i=0; i<tmp_len; i++)
{
// 返回緩存區當前指針所指數據
if(1 == type)
buf[i] = usart1_receive_data();
else
buf[i] = usart3_receive_data();
buf_len++;
// 超過約定的上限長度
if(buf_len >= 250)
{
buf_len=0;
break;
}
}
// 數據不為空
if(0 != buf_len)
{
// 串口數據發送
if(1 == type)
usart3_send_bytes(buf, buf_len);
else
{
usart1_send_bytes(buf, buf_len);
printf("\r\n");
// 如果沒有登錄
if(0 == login)
{
// 檢查登錄
if(0 == check_login(buf, buf_len))
{
login = 1;
printf("登錄成功\r\n");
}
else if(2 == check_login(buf, buf_len))
{
printf("賬號或密碼超長\r\n");
}
else if(3 == check_login(buf, buf_len))
{
printf("命令過短,請發送命令\"login#賬號#密碼#\"登錄\r\n");
}
else
{
printf("賬號或密碼錯誤,請發送命令\"login#賬號#密碼#\"登錄\r\n");
}
}
// 已經登錄
else
{
cmd_analysis(buf, buf_len);
}
}
}
}
// 檢查登錄 傳入收到的數據 返回 0驗證成功,1賬號或密碼錯誤,2數據超長,3數據過短
u8 check_login(u8* buf, u8 len)
{
u8 i = 0, j = 0;
u8 str_username[21] = {0};
u8 str_password[21] = {0};
if(len < 9)
{
return 3;
}
// 登錄命令
if(buf[0] == 'l' && buf[1] == 'o' && buf[2] == 'g' && buf[3] == 'i' && buf[4] == 'n' && buf[5] == '#')
{
// 解析數據獲取username和password 分隔符為'#'
j = 0;
i = 6;
while(buf[i] != '#' && i < len)
{
// 數據超長
if(j >= 20)
{
return 2;
}
str_username[j++] = buf[i++];
}
str_username[j] = '\0';
j = 0;
i++;
while(buf[i] != '#' && i < len)
{
// 數據超長
if(j >= 20)
{
return 2;
}
str_password[j++] = buf[i++];
}
str_password[j] = '\0';
if(0 == strcmp((char *)str_username, (char *)username) && 0 == strcmp((char *)str_password, (char *)password))
{
return 0;
}
else
{
return 1;
}
}
else
{
return 1;
}
}
/*
函數功能: 命令解析
命令格式: cmd#device#status#
返回: 0不符合規則 1解析成功
*/
u8 cmd_analysis(u8* buf, u8 len)
{
u8 device[10] = {0};
u8 status[6] = {0};
u8 i = 0, j = 0;
if(len < 7)
{
printf("命令過短\r\n");
return 0;
}
// 命令格式校驗
if(buf[0] == 'c' && buf[1] == 'm' && buf[2] == 'd' && buf[3] == '#')
{
// 解析數據獲取devicestatus 分隔符為'#'
j = 0;
i = 4;
while(buf[i] != '#' && i < len)
{
// 數據超長
if(j >= 9)
{
printf("device超長\r\n");
return 0;
}
device[j++] = buf[i++];
}
device[j] = '\0';
j = 0;
i++;
while(buf[i] != '#' && i < len)
{
// 數據超長
if(j >= 5)
{
printf("status超長\r\n");
return 0;
}
status[j++] = buf[i++];
}
status[j] = '\0';
// LED1的命令 ON/OFF
if(0 == strcmp((char *)device, "LED1"))
{
if(0 == strcmp((char *)status, "ON"))
{
LED1 = 0;
printf("LED1打開\r\n");
return 1;
}
else if(0 == strcmp((char *)status, "OFF"))
{
LED1 = 1;
printf("LED1關閉\r\n");
return 1;
}
else
{
printf("命令錯誤\r\n");
return 0;
}
}
else if(0 == strcmp((char *)device, "BEEP"))
{
if(0 == strcmp((char *)status, "ON"))
{
BEEP = 1;
printf("BEEP打開\r\n");
return 1;
}
else if(0 == strcmp((char *)status, "OFF"))
{
BEEP = 0;
printf("BEEP關閉\r\n");
return 1;
}
else
{
printf("命令錯誤\r\n");
return 0;
}
}
else if(0 == strcmp((char *)device, "login"))
{
if(0 == strcmp((char *)status, "out"))
{
login = 0;
printf("賬號登出\r\n");
return 1;
}
else
{
printf("命令錯誤\r\n");
return 0;
}
}
else
{
printf("命令錯誤\r\n");
return 0;
}
}
else
{
printf("命令錯誤\r\n");
return 0;
}
}
usart1.c
#include "usart1.h"
#include "stdio.h"
static uint8_t usart1_buffer[255];
static uint8_t usart1_index;
static uint8_t usart1_count;
//加入以下代碼,支持printf函數,而不需要選擇use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//標准庫需要的支持函數
struct __FILE
{
int handle;
};
FILE __stdout;
//定義_sys_exit()以避免使用半主機模式
void _sys_exit(int x)
{
x = x;
}
//重定義fputc函數
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循環發送,直到發送完畢
USART1->DR = (u8) ch;
return ch;
}
#endif
// USART1初始化 默認數據位8 停止位1 校驗位none
void usart1_init(u32 bound)
{
// GPIO端口設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* config USART1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1,GPIOA時鍾
/* USART1 GPIO config */
/* Configure USART1 Tx (PA.09) as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化GPIOA.9
/* Configure USART1 Rx (PA.10) as input floating */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA.10
// Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//搶占優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
USART_InitStructure.USART_BaudRate = bound; // 串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No; // 無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 無硬件數據流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 收發模式
USART_Init(USART1, &USART_InitStructure); // 初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 開啟串口接受中斷
USART_Cmd(USART1, ENABLE); // 使能串口1
usart1_index=0;
usart1_count=0;
}
// 中斷服務函數
void USART1_IRQHandler(void)
{
// 接收中斷
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
if(usart1_count==usart1_index)
{
// 緩存區無數據,回到起點開始存儲
usart1_count=0;
usart1_index=0;
}
usart1_buffer[usart1_count]=USART1->DR;
usart1_count++;
if(usart1_count>=250)
{
// 沒有及時取出數據,導致存儲位置到達末尾,回到起點
usart1_count=0;
usart1_index=0;
}
}
}
/*返回緩存區數據的個數*/
uint8_t usart1_getdata_count(void)
{
return usart1_count-usart1_index;
}
/*返回緩存區當前指針所指數據*/
uint8_t usart1_receive_data(void)
{
return usart1_buffer[usart1_index++];
}
/*串口數據發送函數
data_send:發送數據
*/
void usart1_send_byte(uint8_t data_send)
{
USART_SendData(USART1, data_send);
while (!(USART1->SR & USART_FLAG_TXE));
}
/*串口數據發送函數
data_buffer:發送數據串的首地址
length:發送數據的長度
*/
void usart1_send_bytes(uint8_t* data_buffer,uint8_t length)
{
uint8_t i;
for(i=0; i<length; i++)
{
usart1_send_byte(data_buffer[i]);
}
}