STM32
直播中

深圳市正商电子科有限公司

8年用户 463经验值
私信 关注
[问答]

如何去实现STM32串口通信的超级终端控制LED灯的设计

如何去实现STM32串口通信的超级终端控制LED灯的设计?有哪些基本操作?

回帖(1)

tijing忽忽

2021-12-7 10:10:56
一、硬件介绍
本程序使用开发板:STM32-PZ6806L
1、GPIO控制LED
开发板中LED的硬件电路参看:直接通过寄存器地址操作控制LED灯
2、串口
开发板中连接了MCU的2个串口,分别为USART1和USART3,其中USART1通过CH340G接PC端USB口,实现USB转串口功能,可以用于程序下载和串口通信,但通过PC端的超级终端连接时不能连接,所以本程序使用开发板上的另一个串口USART3,该串口信号转换成RS232,通过直连串口线与PC端的COM口相连,可以实现与超级终端通信。
开发板串口的硬件连接图请参考:STM32串口通信之Hello
二、项目创建与配置
请参看《STM32串口通信之Hello》中的“使用库函数的串口程序项目配置”,在此基础上,在"User"文件夹下新建"Led"文件夹,并将该文件夹配置在"C/C++"选项卡中的"Include Paths"包含文件路径中。
三、LED灯控制
1、新建"Led.h"文件并保存在"User/Led"文件夹下,内容如下:

#ifndef __LED__H
#define __LED__H
#include "stm32f10x.h"
#define LED_PORT     GPIOC
#define LED_PIN        (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7)
#define LED_PORT_RCC RCC_APB2Periph_GPIOC
#define LED1 GPIO_Pin_0
#define LED2 GPIO_Pin_1
#define LED3 GPIO_Pin_2
#define LED4 GPIO_Pin_3
#define LED5 GPIO_Pin_4
#define LED6 GPIO_Pin_5
#define LED7 GPIO_Pin_6
#define LED8 GPIO_Pin_7

void LED_Init(void);
void LED_On(u8 x, u8 en);
#endif
2、新建"Led.c"文件并保存在"User/Led"文件夹下,内容如下:

#include "led.h"
void LED_Init()
{
         GPIO_InitTypeDef GPIOC_0_mode;
         RCC_APB2PeriphClockCmd( LED_PORT_RCC, ENABLE );           //使能GPIOC时钟
         GPIOC_0_mode.GPIO_Pin = LED_PIN;
         GPIOC_0_mode.GPIO_Speed = GPIO_Speed_50MHz;
         GPIOC_0_mode.GPIO_Mode = GPIO_Mode_Out_PP;
         GPIO_Init(GPIOC, &GPIOC_0_mode);
}
void LED_On(u8 x, u8 en)
{
         uint16_t led[] = {LED1,LED2,LED3,LED4,LED5,LED6,LED7,LED8};
         if(x<0x31 || x>0x38)
                   return;
         if(en=='y')
                   GPIO_ResetBits(LED_PORT,led[x-0x31]);
         else
                   GPIO_SetBits(LED_PORT,led[x-0x31]);
}
LED_Init函数使能GPIOC时钟,设置GPIOC_0~GPIOC_7为推挽输出模式,LED_On函数根据参数x和en来设置单个LED灯的亮灭状态,调用函数GPIO_ResetBits来复位管脚,即点亮LED灯,调用函数GPIO_SetBits来置位管脚,即灭LED灯。x参数为第几个LED灯的数字字符,en参数为'y'字符或'n'字符,'y'表示点亮,'n'表示灭灯。
四、串口通信程序
该串口通信采用USART3,发送数据采用查询方式,接收数据采用中断方式,所以需要使能GPIOB和USART3时钟、配置中断、配置USART3、使能中断和使能USART3等操作。
1、创建"usrt.h"文件并保存在"User/Uart"文件夹下,内容为:

#ifndef __UART__H
#define __UART__H
#include "stm32f10x.h"
#include "stdarg.h"
#include "stdlib.h"
#include "string.h"
#include "stdio.h"        
#define EN_USART1 0         //禁用USART1
#define EN_USART3 1         //启用USART3

#define USART_n                 USART3    //使用USART3用于fputc

#define USART1_REC_LEN                          200
extern u8  USART1_RX_BUF[USART1_REC_LEN];
extern u16 USART1_RX_STA;  

#define USART3_REC_LEN                          200
extern u8  USART3_RX_BUF[USART3_REC_LEN];
extern u16 USART3_RX_STA;  

void USART1_Config(u32 baud);
void USART1_printf(char* fmt,...);

void USART3_Config(u32 baud);
void USART3_printf(char* fmt,...);
#endif
2、创建"usrt.c"文件并保存在"User/Uart"文件夹下,内容为:

#include "uart.h"

//重写fputc函数
int fputc(int ch, FILE *f){      
         while(USART_GetFlagStatus(USART_n, USART_FLAG_TC)!=SET);
  USART_SendData(USART_n, (unsigned char)ch);     
         return ch;
}

#if EN_USART1
u8 USART1_RX_BUF[USART1_REC_LEN];     //接收缓冲区,最大USART_REC_LEN个字节.
//接收标志
//bit15,        置位表示接受完成
//bit14,        置位表示接收到0x0d
//bit13~0,   接收字符个数
u16 USART1_RX_STA=0;       //16位接收记录标志全局变量
void USART1_Config(u32 baud)
{
         GPIO_InitTypeDef GPIO_InitStructure;
         USART_InitTypeDef USART_InitStructure;
         NVIC_InitTypeDef NVIC_InitStructure;
         //Enable GPIOA and USART1
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
         //Config GPIOA_9 and GPIOA_10
         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);
         //USART1 NVIC config
         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);      //配置NVIC中断分组2:2位抢占,2位响应
         NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;            //子优先级3
         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                            //IRQ通道使能
         NVIC_Init(&NVIC_InitStructure);          //初始化NVIC寄存器
         //Config USART1
         USART_InitStructure.USART_BaudRate = baud;
         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);
         USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//使能USART1的接收中断
         //Enable USART1
         USART_Cmd(USART1, ENABLE);
}

void USART1_IRQHandler(void){ //USART1中断函数
         u8 Res;
         //(USART1_RX_STA&0x3FFF)为数据的长度(不包括回车符)
         //当(USART1_RX_STA&0xC000)为真时表示数据接收完成(超级终端按下回车键),
         //读到0x0d 0x0a表示数据结束
         //在main函数中处理完接收数据后将USART_RX_STA置0
                   printf("a");
         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
         {   
                   Res =USART_ReceiveData(USART1);//接收一个字符
                   printf("%c",Res); //会送给PC
                   if((USART1_RX_STA & 0x8000)==0)            //本次接收未完成
                   {                                 
                            if(USART1_RX_STA & 0x4000)                     //已经接收到了0x0d
                            {                                                              
                                     if(Res!=0x0a)      //如果已经接收了回车符,而本次接收的不是换行符,则数据出错
                                               USART1_RX_STA = 0;      
                                     else
                                               USART1_RX_STA |= 0x8000;      //标记数据接收完成
                            }
                            else   //没有接收过0X0D表示为信息字符
                            {                                 
                                     if(Res==0x0d)    //本次接收是0x0d,则在USART1_RX_STA中标记
                                               USART1_RX_STA |= 0x4000;
                                     else   //本次接收不是0x0d,则确定是信息字符,存入数组中
                                     {
                                               USART1_RX_BUF[USART1_RX_STA & 0X3FFF]=Res ; //字符存入数组中
                                               USART1_RX_STA++;         //数据长度加1
                                               if(USART1_RX_STA > (USART1_REC_LEN-1))   //如果数据长度超过缓冲区大小,出错,重新接收
                                                        USART1_RX_STA=0;
                                     }               
                            }
                   }               
         }
}
#endif

#if EN_USART3
u8 USART3_RX_BUF[USART3_REC_LEN];     //接收缓冲区,最大USART_REC_LEN个字节.
//接收标志
//bit15,        置位表示接受完成
//bit14,        置位表示接收到0x0d
//bit13~0,   接收字符个数
u16 USART3_RX_STA=0;       //16位接收记录标志全局变量
void USART3_Config(u32 baud)   //初始化UASRT3
{
  //GPIO端口设置
  GPIO_InitTypeDef GPIO_InitStructure;
         USART_InitTypeDef USART_InitStructure;
         NVIC_InitTypeDef NVIC_InitStructure;
                    
         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能UART3对应的GPIOB
         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); //使能串口的RCC时钟

         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11; //配置USART3的RX引脚PB11
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
         GPIO_Init(GPIOB, &GPIO_InitStructure);

         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //配置USART3的TX引脚PB10
         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //服用推挽输出
         GPIO_Init(GPIOB, &GPIO_InitStructure);

   //Usart3 NVIC 配置
         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;            //子优先级3
         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                            //IRQ通道使能
         NVIC_Init(&NVIC_InitStructure);          //初始化NVIC
   //USART3 初始化设置
         USART_InitStructure.USART_BaudRate = baud;//波特率;
         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); //初始化串口
         USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);//使能接收中断
         USART_Cmd(USART3, ENABLE);                    //使能串口
}

void USART3_IRQHandler(void)  //USART3中断函数
{     
         //(USART1_RX_STA&0x3FFF)为数据的长度(不包括回车符)
         //当(USART1_RX_STA&0xC000)为真时表示数据接收完成(超级终端按下回车键),
         //读到0x0d 0x0a表示数据结束
         //在main函数中处理完接收数据后将USART_RX_STA置0
         u8 Res;
         //       printf("a");
         if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
         {   
                   Res =USART_ReceiveData(USART3);//接收一个字符
                   printf("%c",Res); //回送给PC
                   if((USART3_RX_STA & 0x8000)==0)            //本次接收未完成
                   {                                 
                            if(USART3_RX_STA & 0x4000)                     //已经接收到了0x0d
                            {                                                              
                                     if(Res!=0x0a)      //如果已经接收了回车符,而本次接收的不是换行符,则数据出错
                                               USART3_RX_STA = 0;      
                                     else
                                               USART3_RX_STA |= 0x8000;      //标记数据接收完成
                            }
                            else   //没有接收过0X0D表示为信息字符
                            {                                 
                                     if(Res==0x0d)    //本次接收是0x0d,则在USART1_RX_STA中标记
                                               USART3_RX_STA |= 0x4000;
                                     else   //本次接收不是0x0d,则确定是信息字符,存入数组中
                                     {
                                               USART3_RX_BUF[USART3_RX_STA & 0X3FFF]=Res ; //字符存入数组中
                                               USART3_RX_STA++;         //数据长度加1
                                               if(USART3_RX_STA > (USART3_REC_LEN-1))   //如果数据长度超过缓冲区大小,出错,重新接收
                                                        USART3_RX_STA=0;
                                     }               
                            }
                   }               
         }
}
#endif

3、"main.c"程序

#include "uart.h"
#include "led.h"
int main()
{
         u8 ch1, ch2, i;
         LED_Init();//LED GPIO初始配置
         USART3_Config(115200); //USART1配置,接收中断
         USART3_RX_STA = 0xC000; //数据初始为接收回车状态
         for(i=0;i<8;i++)  //将8个LED初始设为灭灯状态
                   LED_On(i+0x31,'n');
         while(1)
         {
                   if(USART3_RX_STA & 0xC000)//接收到回车-0xC000
                   {
                            if((USART3_RX_STA & 0x3FFF)==0) //数据长度为0,显示菜单
                            {
                                     printf("\033[1;47;33mrn"); //设置超级终端文字颜色
                                     printf(" xy--开LEDx灯     xn--灭LEDx灯 rn");
                                     printf(" 请输入控制指令,按回车键执行! \033[0mrn");
                            }
                            else if((USART3_RX_STA&0x3FFF)==2)
                            {
                                     ch1 = USART3_RX_BUF[0];
                                     ch2 = USART3_RX_BUF[1];
                                     if(      ch1>='1' && ch1<='8'&&( ch2=='y' || ch2=='n'))
                                     {
                                               LED_On(ch1, ch2);
                                               printf("LED%c%srn",ch1,ch2=='y'?"开了":"关了");
                                     }
                                     else
                                               printf("命令错误rn");
                            }
                            USART3_RX_STA = 0; //完成一次操作,复位
                   }
         }
}
在此程序中分别对USART1和USART3实现了同样的操作,这里实际用到的是USART3,本程序参考杜洋老师的程序实现。

五、项目编译、开发板设置及程序运行
1、在项目的"User"组中加入"led.c"和"uart.c"文件,编译,连接生成.hex文件;
2、将开发板中的P232的两个跳线帽连接到COM3;
3、将程序下载到开发板;
4、在PC机上运行"HyperTerminal"程序,新建连接:

  

  

HyperTerminal运行如下:

  

  

超级终端上的显示与开发板上LED灯的状态一致。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分