vc++ cserialport 打开多个串口_STM32之USART串口

a643e6558563590f168cd930cb6fcb4f.png

1. 简介

串口全称:Universal synchronous asynchronous receiver transmitter,中文名称:通用同步异步串行接收发送器。串口可用于接收和发送数据,可以用作跟其他设备通讯,如蓝牙模块、GSM模块、GPS模块等。
串口中最常见的是RS-232,共有9根线用于通讯,但常用的仅3根线:RXD、TXD、GND。其中RXD用于接收,TXD用于发送,GND为信号地线,两个通讯设备之间的收发信号(RXD与TXD)应交叉相连。

2. 基本概念

  • 波特率:串口异步通讯中由于没有时钟信号,所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,常见的波特率为4800、9600、115200 等。波特率越高传输速度越快,但出错的概率要更大。
  • 通讯的起始和停止信号:串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5或 2个逻辑 1的数据位表示,只要双方约定一致即可。
  • 有效数据:在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7或 8位长。
  • 数据校验:在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0校验(space)、1校验(mark)以及无校验(noparity)。奇校验要求有效数据和校验位中“1”的个数为奇数,偶校验与奇校验要求刚好相反,要求帧数据和校验位中“1”的个数为偶数,0校验是不管有效数据中的内容是什么,校验位总为“0”,1校验是校验位总为“1”。
  • 常用的设置:波特率9600,8位数据位,1位停止位,无校验位,无硬件流控。

串口USART分为两部分,初始化和处理。

1. 初始化分三步:GPIO、通用中断、USART。

1.1. GPIO:时钟、引脚、输入输出模式、端口初始化

  • 时钟:需要同时使能GPIO时钟
  • 引脚:需要选择指定的引脚
  • 输入输出模式:TX引脚选择复用推挽模式,RX引脚选择浮空输入模式
  • 端口初始化:初始化指定的端口

1.2. 通用中断:优先级分组、中断源、优先级、使能

  • 优先级分组:设定合适的优先级分组
  • 中断源:选择指定的中断源:USART1_IRQn
  • 优先级:设定合适的优先级
  • 使能:调用库函数即可,可选择使能多个中断,一般情况下只使能接收中断。

1.3. USART:时钟、波特率、数据字长、停止位、校验位、硬件流控制、工作模式、初始化、使能。

  结构体

typedef struct {
uint32_t USART_BaudRate;
uint16_t USART_WordLength;
uint16_t USART_StopBits; 
uint16_t USART_Parity; 
uint16_t USART_Mode;
uint16_t USART_HardwareFlowControl; 
} USART_InitTypeDef;
  • 时钟:需要使能USART1时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

  • 波特率:一般可选的设置项包括4800、9600、115200等,常用的波特率为9600。

USART_InitStructure.USART_BaudRate = 9600;

  • 数据字长:可选8位或9位,常用的是8位。

USART_InitStructure.USART_BaudRate = 9600;

  • 停止位:可选0.5、1、1.5、2个,常用的是1个停止位。

USART_InitStructure.USART_BaudRate = 9600;

  • 奇偶校验:可选无校验、奇校验和偶校验,常用的是无校验。

USART_InitStructure.USART_Parity = USART_Parity_No;

  • 模式:可选发送、接收,或者同时启用,一般都是同时启用。

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  • 硬件流控:可选有使能RTS、使能CTS、同时使能、不使能,一般选择不使能。

USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

  • 初始化配置

USART_Init(USART1, &USART_InitStructure);

  • 使能

USART_Cmd(USART1, ENABLE);

2. 处理

处理分为接收和发送。
接收处理通常放在中断中,中断服务函数名称为USART1_IRQHandler(),调用库函数USART_ReceiveData()得到接收的数据,并自动清除接收中断标志位;
发送直接调用库函数USART_SendData()即可,发送完成之后会调用函数来获取相应标志来实现发送完成等待功能,即USART_SendData()函数返回时,相应的数据已经确保发送完成,并可发送下一个数据,发送不同类型的数据可以封装成不同的函数,但最终都是调用USART_SendData()函数。


接收:

// 串口中断服务函数
void USART1_IRQHandler(void)
{
    uint8_t ucTemp;
    if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)
    {    
        ucTemp = USART_ReceiveData(USART1);
    }     
}

发送:

//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t ch)
{
    //发送一个字节数据到USART
    USART_SendData(pUSARTx,ch);
    //等待发送数据寄存器为空,即发送完成
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET); 
}
//发送一个数组
void Usart_SendArray(USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
//发送字符串
void Usart_SendString(USART_TypeDef * pUSARTx, char *str)

完整代码(仅自己编写的部分)

void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 嵌套向量中断控制器组选择 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    /* 配置USART为中断源 */
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    /* 抢断优先级*/
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    /* 子优先级 */
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    /* 使能中断 */
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    /* 初始化配置NVIC */
    NVIC_Init(&NVIC_InitStructure);
}

void USART_Config(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 打开串口GPIO的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 打开串口外设的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // 将USART Tx的GPIO配置为推挽复用模式
    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);

  // 将USART Rx的GPIO配置为浮空输入模式
    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_Configuration();

    // 使能串口接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    // 使能串口
    USART_Cmd(USART1, ENABLE);
}

//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx, uint8_t ch)
{
    //发送一个字节数据到USART
    USART_SendData(pUSARTx,ch);
    //等待发送数据寄存器为空,即发送完成
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

//发送数组
void Usart_SendArray(USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
    uint8_t i;

    for(i = 0; i < num; i++)
    {
        /* 发送一个字节数据到USART */
        Usart_SendByte(pUSARTx, array[i]);
    }
    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC)==RESET);
}

//发送字符串
void Usart_SendString(USART_TypeDef * pUSARTx, char *str)
{
    uint32_t k = 0;

    do
    {
        Usart_SendByte(pUSARTx, *(str + k));
        k++;
    } while(*(str + k)!='0');

    /* 等待发送完成 */
    while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

//发送16位数
void Usart_SendHalfWord(USART_TypeDef * pUSARTx, uint16_t ch)
{
    uint8_t temp_h, temp_l;

    /* 取出高八位 */
    temp_h = (ch & 0xFF00) >> 8;
    /* 取出低八位 */
    temp_l = ch & 0xFF;

    /* 发送高八位 */
    USART_SendData(pUSARTx, temp_h);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

    /* 发送低八位 */
    USART_SendData(pUSARTx, temp_l);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

// 串口中断服务函数
void USART1_IRQHandler(void)
{
    uint8_t ucTemp;

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        ucTemp = USART_ReceiveData(USART1);
        USART_SendData(USART1, ucTemp);
    }
}

int main(void)
{
    USART_Config(9600);

    Usart_SendString(USART1, "Hello World!n");

    while(1)
    {
    }
}

仿真结果

1. 发送

编译完成后,点击

957759a8b0ecc14863b7c479d7a91dfe.png

开始仿真,点击

b9863fc53c7ce3bb70fd305c185a4858.png

,打开串口监视串口(点击右侧的箭头可以选择第几个串口),然后运行程序,可以在串口监视窗口看到串口发送的信息。

4903b31ae3a42cee457ff5113a3d38ec.png

2. 接收

如果想要仿真串口接收,需要使用虚拟串口软件vspd,下载安装完成后,运行该软件:

b531b3c4d10552dda3478423acbe0644.png


可以看到左侧有COM1-5共 5个物理串口,点击右侧的添加端口,可以添加COM6和COM7两个虚拟串口,并自动连接这两个串口。

3110a7f44c0fada30088b58c29aa1ce5.png


此时可以打开2个串口工具,分别设置为COM6和COM7,并分别发送数据,看看对方是否能够正常接收,如果可以,表示虚拟串口添加并连接成功,此时在串口工具中关闭COM6。

3f1af8da5614c3e3ceb8218af92231eb.png

运行KEIL,并开始仿真后,在COMMAND串口中输入
MODE COM6 9600,0,8,1
ASSIGN COM6 <S1IN> S1OUT
第一条命令设置COM6波特率9600,无校验位,8个数据位,1个停止位,注意这里的设置应该与程序中串口的设置保持一致。
第二条命令将COM6和单片机的第一个串口进行绑定,即单片机第一个串口的收发数据通过计算机的COM6进行收发。
此时在串口工具打开COM7,并通过串口工具发送数据,程序即可接收到发送的数据。

a320b4ce69a1dd012cf6730f8c3a81fb.png


从上图可以看到从COM7发送了一个A给单片机,单片机接收到了A,并将A又发送出去,COM7接收到了A。
因此,通过虚拟串口软件,即可在无开发板、无串口线、无串口设备的情况下,通过串口工具即可调试串口接收及发送程序,实现纯软件环境调试。

学习资料分享交流群:1093776732 入群有全套学习视频资料电子书免费赠送!

参考资料:

嵌入式开发直播课 - STM32 USART串口的应用​www.makeru.com.cn
a3211e628e999d2db246c94771e586af.png
物联网开发入门直播课 - 基于STM32讲解串口操作​www.makeru.com.cn
a3211e628e999d2db246c94771e586af.png
物联网开发入门直播课 - 通过Z-stack协议栈实现串口透传​www.makeru.com.cn 嵌入式开发直播课 - 带你揭晓STM32定时器深藏不露的绝技​www.makeru.com.cn
a3211e628e999d2db246c94771e586af.png
物联网STM32入门 - 带你揭晓STM32定时器深藏不露的绝技​www.makeru.com.cn
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC(Microsoft Foundation Class)是一种面向对象的框架,用于开发Windows图形用户界面应用程序。VC 6.0是Visual C++ 6.0的简称,是一种集成开发环境(IDE),用于开发Windows应用程序。串口编程是指对计算机的串行接口进行编程,用来实现与外部设备的数据交互。 在VC 6.0 MFC中进行串口编程,可以通过以下步骤来实现: 1. 包含相关的头文件和库文件:在程序代码中使用#include和#pragma等指令来引用串口编程所需的头文件和库文件。 2. 打开串口:使用相关的函数调用打开要使用的串口。这可以通过调用相关MFC类的成员函数来实现,如CSerialPort类的Open函数。 3. 配置串口参数:使用串口的属性和参数,如波特率、数据位、停止位等进行配置。可以通过调用相关MFC类的成员函数来实现,如CSerialPort类的SetBaudRate和SetDataBits函数。 4. 发送和接收数据:可以通过调用相关MFC类的成员函数,如CSerialPort的Write和Read函数来发送和接收数据。 5. 关闭串口:在程序结束时,需要调用相关函数关闭串口。这可以通过调用相关MFC类的成员函数来实现,如CSerialPort类的Close函数。 需要注意的是,在进行串口编程时,需要考虑到串口的可靠性、错误处理以及通信协议等方面的问题,以确保数据的正确传输。此外,可以使用MFC提供的一些辅助类和函数来简化串口编程的相关操作,如CSerialPort类和相关函数。 总之,VC 6.0 MFC串口编程需要通过调用相关的类和函数来实现串口打开、配置、数据发送和接收等操作,以实现与外部设备的数据交互。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值