socket(三)串口与LED(stm32)

发布时间 2024-01-12 19:53:26作者: 山远尽成云

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