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); } }