socket(三)串口与LED(stm32)
下面通过串口完成点亮LED灯操作
首先,为了节约时间,我们可以先定死意向指令,比如说,用一串字符去替代开关灯的操作,用字符去表示缓冲区长度,所以,我们有了下面的宏定义
#define LED_ON() GPIO_ResetBits(GPIOC, GPIO_Pin_13) #define LED_OFF() GPIO_SetBits(GPIOC, GPIO_Pin_13) #define USART_RX_BUF_LEN 64 //接收缓冲区的长度 u8 USART_RX_BUF[USART_RX_BUF_LEN]; u8 USART_RX_STA = 0;
由于在stm32中没有布尔值,所以我们要通过枚举去自己定义一个布尔值
typedef enum
{
FALSE = 0,
TRUE = 1
} bool;
然后,为了方便各种操作,我们需要两个延时函数
void Delay_us(u32 time)
{
u32 temp;
SysTick->LOAD = 9 * time;
SysTick->VAL = 0X00;
SysTick->CTRL = 0X01;
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && (!(temp & (1 << 16))));
SysTick->CTRL = 0x00;
SysTick->VAL = 0X00;
}
void delay_ms(u16 time)
{
u32 temp;
SysTick->LOAD = 9000 * time;
SysTick->VAL = 0X00;
SysTick->CTRL = 0X01;
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && (!(temp & (1 << 16))));
SysTick->CTRL = 0x00;
SysTick->VAL = 0X00;
}
在stm32当中,最重要的一件事莫过于初始化,一切东西只有初始化后才可以使用,在这个项目里面,我们需要用到串口,LED灯,中断,所以我们需要将这些东西初始化好
下面是LED灯的初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC , ENABLE); //开启时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化结构体
}
下面是串口的初始化
void Usart1_Init(u32 baudrate)
{
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //开启时钟
USART_DeInit(USART1);
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); //初始化结构体
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = baudrate;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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);
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //中断优先级设置
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority= 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
既然串口涉及到了中断函数触发,所以我们来看看中断函数需要做什么。
首先,既然触发了中断,我们还是需要检查是否有中断发生,即是否真的有数据让我们读取。一般我们将需要读取的数据放置在缓冲区,然后再进行处理,缓冲区并不是无限大的,所以我们需要判断缓冲区是否已经满了,如果缓冲区已满,我们需要将缓冲区的数据置0,防止缓冲区满后无法接收到数据。我们将数据送入到缓冲区后,每输入一个字节到缓冲区,缓冲区的索引都需要+1。当我们结束数据输入的标志则为“\r\n”,当我们检测到缓冲区字符出现“\r\n”的组合时,表示这一整行数据都已经被接收完毕,既然我们接收到了命令,就要去解析命令。
// USART1的中断处理函数
void USART1_IRQHandler(void)
{
// 检查是否有接收中断发生(即是否有数据可读)
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
// 如果接收缓冲区已满
if (USART_RX_STA == USART_RX_BUF_LEN)
{
// 将缓冲区索引重置为0,实现循环缓冲
USART_RX_STA = 0;
}
// 将接收到的数据存入缓冲区,并增加缓冲区索引
USART_RX_BUF[USART_RX_STA++] = USART_ReceiveData(USART1);
// 判断接收到的数据是否为'\n'(换行符)和'\r'(回车符)的组合
// 如果是,则表示一整行数据已接收完毕
if (USART_RX_BUF[USART_RX_STA - 1] == '\n' && USART_RX_BUF[USART_RX_STA - 2] == '\r')
{
// 将'\r'替换为字符串结束符'\0',表示字符串的结束
USART_RX_BUF[USART_RX_STA - 2] = '\0';
// 解析并执行命令
parse_command(USART_RX_BUF);
// 重置缓冲区索引
USART_RX_STA = 0;
}
}
}
由于我们这个项目只是操控一个LED灯的原因,所以我们的命令也只关于LED灯的开和关,超出这个命令的指令,我们都需要返回错误信息。拿到正确的指令后,我们需要进行对应的操作,并且完成操作后,我们需要返回一条信息告诉我们,我们已经完成了这条指令
//解析命令
int parse_command(u8 *command)
{
if (strcmp(command, "LEDON") == 0)
{
LED_ON();
Usart1_send_string("LED ON OK!\r\n");
return 0;
}
else if (strcmp(command, "LEDOFF") == 0)
{
LED_OFF();
Usart1_send_string("LED OFF OK!\r\n");
return 0;
}
else
{
Usart1_send_string("ERROR\r\n");
return -1;
}
}
在stm32当中,发送字节只需要调用其本身的函数,然后通过while循环确认一次字符发送完成即可
void Usart1_send_byte(u8 data)
{
USART_SendData(USART1, data);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
而发送字符串只是同样的使用while循环检测到这一句话的结束就可以停止了
void Usart1_send_string(u8 *data)
{
while (*data != '\0')
{
Usart1_send_byte(*data);
data++;
}
}
在stm32中,主函数main()的作用是使用使用各种初始化函数等,确保各个模块可以正常使用即可
int main(void)
{
bool led_state = FALSE;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Usart1_Init(115200);
LED_Init();
while (1)
{
delay_ms(1000);
}
}