历史上的今天
返回首页

历史上的今天

今天是:2024年09月27日(星期五)

正在发生

2019年09月27日 | STM32学习之旅④ USART串口和上位机通信

2019-09-27 来源:eefocus

一、认识其本质

(一)串口

串口是串行接口 (Serial Interface)的简称,它是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位的传送,按位顺序进行,最少只需一根传输线即可完成;成本低但传送速度慢。串行通讯的距离可以从几米到几千米;根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。

(二)协议

所谓协议,就是通信双方约定好的规定,通信双方只有遵守这个规定才能够完成任务。举个栗子就是周幽王烽火戏诸侯,双方约定好以烽火为信号进行通信,但是愚蠢的周幽王为博美人褒姒一笑破坏了这个规定,最后付出的代价是惨重的。可见,通信双方只有遵守协议才能够完成通信。

(三)时序

时序就是协议的实际化,它实质上是一些列的脉冲信号,通信双方将信息按照预先定好的规定(协议)转换成一系列的脉冲信号,通过总线发送给接收方,接收方再将接收到的数据按照规定进行解析,从而得到发送方发送过来的数据。

(四)上位机

上位机和下位机其实是一个相对的概念,上位机指的是可以直接发出操控命令的计算机,一般指PC机,能够显示各种信号变化(液压,水位,温度等),能够将信息直接传递给人。下位机是直接控制设备获取设备状况的计算机,一般是PLC/单片机single chip microcomputer/slave computer/lower computer之类的,下位机需要PC机来对其进行控制。

二、所需材料

USB转TTL串口


串口助手, 密码:07z7


三、USART的介绍

stm32有丰富的通讯外设,USART(Universal Synchronous Asynchronous Receiver Transmitter)、SPI(Serial Peripheral interface)、I2c(Inter-Integrated Circuit)、CAN(Controller Area Network),因为stm32有完整的且强大的固件库,这使得配置串口的难度大大降低了,和用软件IO口模拟通信时序相比,硬件的支持可以大大提高通信的速率、大大降低出错的概率,从而提高了通信的质量和效率。用IO口模拟USART难度较大,它对延时要求比较苛刻,且出错的概率较大,所以一般很少用IO口模拟USART。IO口模拟I2c比较常见,由于I2c的最高通信速度只有3.4M/s,单片机的IO口速度可以完美驾驭。由于SPI多用于一些较高速的通信,例如LCD、OLED、TFT显示器的写入,EEPROM (Electrically Erasable Programmable read only memory)的写入和读取,用IO口模拟效果不是很理想,所以建议使用硬件自带接口。


关于USART,以下是官方的介绍

这里写图片描述

四、USART串口的配置

先来看一下stm32的系统结构


这里写图片描述

通过对stm32几个模块的操作,我们可以发现stm32外设配置的一些基本套路:打开相应的时钟->配置相应的引脚功能->声明对应的结构体->利用相应的Init函数进行初始化


打开打开USATT1、GPIOA、AFIO的时钟


void usart_config()

{

    /*打开USATT1、GPIOA、AFIO的时钟*/

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1

    | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    

    /*配置对应的串口引脚*/

    usart_release_gpio_init();


    /*配置串口中断*/

    usart_para_config();

    

    USART_ClearFlag(USART1,USART_FLAG_TC); //清除发送完成标志位

    NVIC_Config();                         //初始化NVIO

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

}


配置相应的IO口,将其设为复用推挽输出和浮空输入

void usart_release_gpio_init()

{

    GPIO_InitTypeDef GPIO_InitStruct;

    

    /*配置PA9为复用推外输出*/

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

    

    GPIO_Init(GPIOA, &GPIO_InitStruct);

    

    /*配置PA10为浮空输入*/

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;

    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

    

    GPIO_Init(GPIOA, &GPIO_InitStruct);

}


配置NVIC(Nested Vectored Interrupt Controller),即内嵌向量中断控制器,它是用来配置中断抢占优先级和从优先级(响应优先级)的


关于抢占优先级和响应优先级区别:


高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。


抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。


抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。


如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;


void NVIC_Config(void)

{

    NVIC_InitTypeDef NVIC_InitStructure; //NVIC 初始化结构体声明

    

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //设置串口1 中断

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级0

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级为0

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能

    

    NVIC_Init(&NVIC_InitStructure);

}


配置串口协议

void usart_para_config(void)

{

    USART_InitTypeDef USART_InitStruct;

    

    USART_InitStruct.USART_BaudRate = 115200;

    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

    USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8

    USART_InitStruct.USART_Parity = USART_Parity_No;        //N

    USART_InitStruct.USART_StopBits = USART_StopBits_1;     //1


    USART_Init(USART1, &USART_InitStruct);

    

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

}


五、发送函数

(一)单字节发送

在main函数中调用USART_SendData(USART1, 0x08);这个函数就能够完成单字节的发送了

#include "stm32f10x.h"

#include "stm32f10x_gpio.h"

#include "timer.h"

#include "usart.h"

int main()

{

    SystemInit();                //初始化系统,系统时钟设定为72MHz

    systick_init(72);            //配置systick,中断时间设置为72000/72000000 = 1us

    usart_config();

    while(1)

    {

        USART_SendData(USART1, 0x08);

        delay_ms(100);

    }

}


打开串口助手就能够看到串口发来的数据了


这里写图片描述

(二)数据流发送

数据流简单来说就是一串连续的信息序列,一串序列中有若干个字节,每个字节分别对应着通信双方预先约定好的数据含义,例如第一位代表地址、第二位代表数据流向、最后一位代表结束标志、其余位代表数据。数据流的长度可长可短,由通信双方确定,但通信的过程中不能够变化。

定义一个协议栈

typedef struct

{

    u8 head;

    u8 tail;

    u8 direction;

    u8 data[4];

}send_stack;

send_stack tx_stack;

void tx_stack_init()

{

    tx_stack.head = 0xaa;     //协议栈头,起始位,1010 1010b

    tx_stack.direction = 0x09;//数据流方向,0x09表示从单片机发出

    memset(tx_stack.data, 0, sizeof(tx_stack.data));//把tx_stack.data[]全部初始化为零

    tx_stack.tail = 0xdd;     //协议栈尾,结束位,1101 1101b,栈头和栈尾最好能互补

}


将协议栈内的数据依次发出

void usart_senddata()

{

    u8 i;

    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

    USART_SendData(USART1, tx_stack.head);

    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

    USART_SendData(USART1, tx_stack.direction);

    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

    for(i = 0; i < 4; i++)

    {

        USART_SendData(USART1, tx_stack.data[i]);

        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

    }

    USART_SendData(USART1, tx_stack.tail);

    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

}


打开串口助手可以看到串口发来的数据流

这里写图片描述

六、接收函数

接收函数和发送函数类似,先定义接收协议栈

typedef struct

{

    u8 head;

    u8 tail;

    u8 direction;

    u8 data[4];

    u8 lock_flag;

    u8 data_pt;

}receive_stack;

receive_stack rx_stack;

void rx_stack_init()

{

    rx_stack.head = 0x00;         //协议栈头,起始位

    rx_stack.direction = 0x00;    //数据流方向,0x09表示从单片机发出

    memset(rx_stack.data, 0, sizeof(rx_stack.data));//把tx_stack.data[]全部初始化为零

    rx_stack.tail = 0x00;         //协议栈尾,结束位

    rx_stack.data_pt = 0x00;

    rx_stack.lock_flag = UNLOCK;

 }

接收数据需要借助中断来完成

void USART1_IRQHandler(void)

{

    u8 receive_data;

    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断读寄存器是否非空

    {

        receive_data = USART_ReceiveData(USART1);         //接收单个字节的串口数据

        if(rx_stack.lock_flag == UNLOCK)                   //如果接收协议栈未锁柱

        {

            if(receive_data == 0xaa)

            {

                rx_stack.head = receive_data;

            }

            else if(receive_data == 0xf9)

            {

                rx_stack.direction = receive_data;

            }

            else if(receive_data == 0xdd)

            {

                rx_stack.tail = receive_data;

                if(rx_stack.data_pt >= 4)// && (rx_stack.tail == 0xdd))

                {

                    rx_stack.data_pt = 0;

                    rx_stack.lock_flag = LOCK;

                }

            }

            else

            {

                rx_stack.data[rx_stack.data_pt] = receive_data;

                rx_stack.data_pt++;

                if(rx_stack.data_pt > 4)// && (rx_stack.tail == 0xdd))

                {

                    rx_stack.data_pt = 0;

                    rx_stack.lock_flag = LOCK;

                }

            }

        }

        USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接受中断标志

    }

}


将接收到的数据流进行解析,用灯的亮灭将控制命令现实化

void ptr_handle(u8 *stack)

{

    u8 *stack_pt;

    stack_pt = stack;

    if(*stack_pt == 0xff)

    {

        key0.key_change_bit = CHGE_IN;

        if((key0.led_on_off % 2) == 1)

        {

            

        }

        else

        {

            key0.led_on_off = key0.led_on_off >=3 ? 0 : key0.led_on_off + 1;

        }

    }

    else

    {

        key0.key_change_bit = CHGE_IN;

        if((key0.led_on_off % 2) == 1)

        {

推荐阅读

史海拾趣

Apacer公司的发展小趣事

除了产品创新,Apacer宇瞻科技还注重为客户提供量身定制的解决方案。在某次客户系统升级中,宇瞻科技的技术团队发现原有SD卡无法满足大量随机小数据的存取应用,甚至可能缩短SSD使用寿命。于是,宇瞻科技建议客户导入Page Mapping技术,并搭配Smart Read Refresh技术的SD卡,成功解决了客户的存储问题。这一技术突破不仅帮助客户实现了系统升级,也为宇瞻科技赢得了更多的信任和赞誉。

Dialog Semiconductor(戴乐格半导体)公司的发展小趣事

Dialog Semiconductor一直专注于能源管理领域,致力于提供高效的电源管理解决方案。公司推出了多种高效的电源管理芯片,这些芯片能够显著延长电池寿命并提高设备效率。特别是在移动设备领域,Dialog Semiconductor的电源管理芯片被广泛应用于智能手机、平板电脑等设备中,为用户提供了更长的使用时间和更好的使用体验。

AB Connectors Ltd公司的发展小趣事

随着公司实力的不断增强,AB Connectors Ltd开始将目光投向国际市场。公司积极参加国际电子展会和商务洽谈活动,与海外客户建立了广泛的合作关系。同时,公司还通过设立海外办事处和建立分销网络等方式,进一步拓展国际市场。这些举措不仅提高了公司的知名度和影响力,还为公司的快速发展提供了有力支持。

Daewoo公司的发展小趣事

然而,好景不长。1997年亚洲金融危机的爆发给Daewoo公司带来了前所未有的挑战。由于过度扩张和错误的投资决策,公司背负了巨额债务,资不抵债。面对这一困境,Daewoo公司不得不进行资产重组和裁员等措施,以减轻财务压力。虽然这些措施在短期内带来了痛苦,但也为公司的未来发展奠定了基础。

Aimtec公司的发展小趣事

在电子行业的发展过程中,Aimtec公司积极寻求与行业领军企业的合作机会。通过与这些企业的合作,Aimtec不仅获得了更多的市场资源和技术支持,还提升了自身的技术水平和创新能力。同时,这些合作也为Aimtec带来了更多的商业机会和发展空间,实现了双方的共赢发展。

FCI connector [富加宜连接器]公司的发展小趣事

FCI Connector的前身可以追溯到1937年成立的费城绝缘体公司(Philadelphia Insulation Company)。这家公司最初专注于电子零件的制造和销售。随着时间的推移,公司在1976年被德州仪器(Texas Instruments)收购,并更名为TI Electronic Components(TIEC)。然而,TIEC在1981年做出了一个重大的战略决策:将连接器业务剥离出来,成立了FCI Connector公司。这一决策让FCI能够更专注于电子连接器的研发、生产和销售,为其后续的快速发展奠定了基础。

问答坊 | AI 解惑

用CPLD控制曼彻斯特编解码器

摘要:讨论如何使用CPLD实现单片机与曼彻斯特编解码器的接口。设计时采用自顶向下的流程,具体电路可灵活地添加到各种曼彻斯特码接口系统中。 关键词:曼彻斯特编解码器 T2模式 T5模式 引 言   在油田测井中,井下仪在井下采集大量信息,并传 ...…

查看全部问答>

《电子工程师手册-常用资料》

《电子工程师手册-常用资料》…

查看全部问答>

目标设计平台宣传册.pdf

目标设计平台宣传册.pdf…

查看全部问答>

狂想2010:Linux之父受雇于微软

留下你的预言吧,因为许多预言也许将会成为现实,开源业同样如此 且让我们听听国外开源作家的预言。 1、杯具!Linux之父受雇于微软    我将愿意看到Linux之父李纳斯·托沃兹(Linus Torvalds)受雇于微软,在雷德蒙总部负责领导一场真正的Linu ...…

查看全部问答>

OPEN_DEVICE问题

hDev = CreateFile(NDISUIO_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,0);         if(hDev == INVALID_HANDLE_VALUE) {                 CString s; &nbs ...…

查看全部问答>

显示菜单

怎么在新建的一个CE程序中,显示出一个菜单来呢?…

查看全部问答>

谁有LPC2214的通讯协议,或者ISP的通讯协议。能不能给我提供一份啊????急。。。。。。

谁有LPC2214的通讯协议,或者ISP的通讯协议。能不能给我提供一份啊????急。。。。。。 现在在弄ARM,但是不知道通讯协议,哪位大虾给我提供一份吧,我EMail:zengkun258@126.com…

查看全部问答>

基于FPGA的数字电压表的设计的程序

紧急求助:测0~100v直流电压,分辨率为0.02V的,LCD液晶显示,用VHDL编程,很急  辛苦了,谢谢…

查看全部问答>

请教:转速测试用的磁钢怎么确定规格与磁通?

您好!        请教:转速测试用的磁钢怎么确定规格与磁通大小?                  另请问哪里有这种磁钢卖呀?…

查看全部问答>

【一】【FPGA助学系列-准备篇】软件下载及开箱说明

网盘链接更新,20130815为了大家在相同的开发环境下进行开发,以减少兼容性问题的发生,建议统一安装Altera的Quartus11.1 +SP2版本的开发软件和ModelSim6.5版本的仿真软件。原因主要有: 1、        当然是目前我使 ...…

查看全部问答>