一、微项目实现目标:
电脑端发送数据执行,比如:“LED ON”、“LED OFF”,STM32通过获取到数据后,相应对应数据指令,并执行开灯和关灯操作;
二、微项目硬件配置需求:
stm32F103C8T6核心板一块
0.96寸OLED显示,用于显示计数
CH340一块,用于笔记本到串口的传输
LED灯接入都PA1端口
三、前置知识:
1,文本数据包接受,状态机设定
①当获取到‘@’字符认定为包头,从S=0切到S=1,开始接受数据
②在S=1状态下,当没接受到‘\r’字符,则认定这个数据段的数据,都要接受存放到字符数组中
③在接到‘\r’字符时,则认为实际数据段接受完毕,并且状态切换为S=2;
④在S=2时刻,当收到’\n‘,则认为本轮数据包整体上接受完毕,S=2切换到S=0,为下次进入状态机做准备;
2,创建发送函数,并且封装为发送字符串数据(当检测到\0时刻,认为本轮数据传输完毕);
3,创建接受函数,并在内部执行状态机
四、代码逻辑分析:
1,配置USART初始化模块
①打开USART1时钟,打开GPIO时钟
②配置PA9和PA10参数,其中tx配置为复用推挽输出,rx配置为上拉输入模式
③配置USART参数,包括:模式、硬件流控制、传送数据字长、数据位、校验位、停止位
④开启USART中断(准备在中断中进行接受数据包处理,接受数据时刻,触发中断)
⑤配置中断优先级分组
⑥配置NVIC
⑦开启USART
2,发送数据模块,并且封装为发送为一个数组。
①主要需要关注的是,发送一个数据位,需要等待发送寄存器为空,才认为数据已经发送;
②封装字符数组发送
3,接受数据包相对就复杂的多,在中断函数中进行配置
①中断函数配置USART1_IRQHandler,中断服务函数的名称一定要与启动文件中的要保持一致;
4,状态机数据流,在中断服务函数中执行
5,配置LED模块
①开启GPIOA-PA1的时钟
②配置PA1端口配置,设置为推挽输出端口;
③配置为默认高电平,在硬件配置上,默认低电平点亮LED
6,字符串比较函数,strcmp使用,用于比较接受到的数据;(记得包含cstring的头文件,函数封装在此处)
相等于LED ON,点亮
相等于LED OFF,熄灭
其他,不响应
五、代码示例:
1,配置USART初始化模块
①打开USART1时钟,打开GPIO时钟
②配置PA9和PA10参数,其中tx配置为复用推挽输出,rx配置为上拉输入模式
③配置USART参数,包括:模式、硬件流控制、传送数据字长、数据位、校验位、停止位
④开启USART中断(准备在中断中进行接受数据包处理,接受数据时刻,触发中断)
⑤配置中断优先级分组
⑥配置NVIC
⑦开启USART
void serial_init(void)
{
//开启GPIO、USART 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//配置GPIO端口,仅使用TX端口,PA9配置为复用推挽输出端口
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,& GPIO_InitStruct);
//开启RX通道,上拉输入
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,& GPIO_InitStruct);
//配置usart
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate=9600;
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;//开启RX和TX
USART_InitStruct.USART_Parity=USART_Parity_No ;
USART_InitStruct.USART_StopBits=USART_StopBits_1;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_Init( USART1, &USART_InitStruct);
//开启USART中断
USART_ITConfig( USART1,USART_IT_RXNE, ENABLE);
//配置NVIC优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//配置NVIC
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn ;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStruct);
//开启USART
USART_Cmd(USART1,ENABLE);
}
2,发送数据模块,并且封装为发送为一个数组。
①主要需要关注的是,发送一个数据位,需要等待发送寄存器为空,才认为数据已经发送;
②封装字符数组发送
void senddata(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while(USART_GetFlagStatus( USART1,USART_FLAG_TXE)==RESET);
}
//发送字符串
void sendstring(char *str)
{
uint8_t ptr=0;
while(str[ptr]!='\0')
{
senddata(str[ptr++]);
}
}
3,接受数据包相对就复杂的多,在中断函数中进行配置
①中断函数配置USART1_IRQHandler,中断服务函数的名称一定要与启动文件中的要保持一致;
4,状态机数据流,在中断服务函数中执行
//接受的中断服务函数
void USART1_IRQHandler(void)
{
static uint8_t re_state=0;
static uint8_t pdata=0;
//静态变量只会初始化一次,与全局变量类似,区别在于静态变量只能在函数内访问
if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET)
{
uint8_t re_data=USART_ReceiveData(USART1);
//状态执行流
if(re_state==0 && re_data=='@')
{
re_state=1;
pdata=0;
}
else if(re_state==1)
{
if(re_data!='\r')
{
receive_data[pdata++]=re_data;
}
else
{
re_state=2;
}
}
else if(re_state==2)
{
if(re_data=='\n')
{
receive_data[pdata]='\0';
re_state=0;
receive_flag=1;
}
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
5,配置LED模块
①开启GPIOA-PA1的时钟
②配置PA1端口配置,设置为推挽输出端口;
③配置为默认高电平,在硬件配置上,默认低电平点亮LED
④设置函数LED开启和关闭
void ledinit(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1 |GPIO_Pin_2 ;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_SetBits(GPIOA,GPIO_Pin_1 |GPIO_Pin_2);
}
void led1_on(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void led1_off(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
6,字符串比较函数,strcmp使用,用于比较接受到的数据;(记得包含cstring的头文件,函数封装在此处)
相等于LED ON,点亮
相等于LED OFF,熄灭
其他,不响应
在主函数中配置
#include "stm32f10x.h" // Device header
#include "delay.h"
#include "OLED.H"
#include "SERIAL.H"
#include <cstdio>
#include "key.H"
#include <cstring>
#include "LED.H"
int main()
{
OLED_Init();
ledinit();
OLED_ShowString(1,1,"send_data:");
serial_init();
// sendstring("adcd");
while(1)
{
if(receive_flag==1)
{
OLED_ShowString(2,1," ");
OLED_ShowString(2,1,receive_data);
if(strcmp(receive_data,"LED_ON")==0)
{
led1_on();
sendstring("led1_on ok!");
}
if(strcmp(receive_data,"LED_OFF")==0)
{
led1_off();
sendstring("led1_off ok!");
}
receive_flag=0;
}
}
}