stm32 串口

目录

简介

串口通讯协议的物理层

电平标准

协议层

USART框图

总结

hal库代码

标准库代码


简介

        USARTUniversal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器。USARTSTM32内部集成的硬件外设,STM32F103系列最多有3个通用同步异步收发器(USART), 2个通用异步收发器( UART)。 USART和UART的主要区别在于, USART支持同步通信,该模式有一根时钟线提供时钟。串口在嵌入式中经常使用,一般使用UART就足够了

串口通讯协议的物理层

        串口通讯的物理层有很多标准及变种,经常提到TTL、 RS232、 RS422、 RS485等标准,简单的说,就是为了适应不同的环境条件,使用了不同的电平标准。一般就使用TTL电平,引脚直接连接即可,假如微处理器在工业现场,需要连接一个几十米外的装置,则应该考虑将TTL电平转为RS232、 RS422、 RS485

        RS-232 电平等标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 标准”的电平信号,才能实现通讯

电平标准

        一般常使用 TTL 的电平标准,为了加大传输距离,可以增加电压、差分传输等方式

TTL接口的串口,硬件连接如下

        TX:数据发送;RX:数据接收;

协议层

        串口通信的数据包由发送设备的 TXD 接口传输到接收设备的 RXD 接口。在串口通信的协
议层中,规定了数据包的内容,它由起始位、主体数据、校验位以及停止位组成,通讯双方的
数据包格式要约定一致才能正常收发数据。

        波特率:一般选波特率都会有9600,19200,115200等选项。其实意思就是每秒传输这么多个比特位数(bit);
        起始位: 先发出一个逻辑”0”的信号,表示传输数据的开始;
        数据位:可以是5~8位逻辑”0”或”1”,先传输bit 0,在传输bit 1,依次类推;
        校验位:数据位加上这一位后,使得“1”的位数应为偶数(偶校验)或奇数(奇校验),以此来校验数据传送的正确性。 校验位是可选的,可以不传输;                        
        停止位:它是一个字符数据的结束标志,数据线变回逻辑”1”;

        一般使用的是8位数据位,1位起始位,1位停止位,无校验位

USART框图

        ① 引脚:主要是tx\rx数据发收,CK:在同步模式时,用于输出时钟;SW_RX:在单线和智能卡模式下接收数据,属于内部引脚,没有具体外部引脚;在硬件流控制时,RTS用于指示本设备准备好可接收数据,低电平说明本设备可以接收数据,CTS则是在低电平说明可以发数据

        ② 波特率发生器:计算波特率公式:波特率=外设时钟频率/USART_BRR寄存器的值*16

USART1挂载APB2上, USART2/3和USART4/5挂载APB1上。APB1时钟最大为36MHz, APB2时钟最大为72MHz。所以要配置USART1波特率为115200则寄存器USART_BRR为72000000/(115200*16)=39.0625

        ③发送器/接收器控制单元:向控制寄存器CR1、 CR2、 CR3和状态寄存器SR写入相应的位,可实现对USART数据的发送和接收控制。

        ④数据收发寄存器单元:发送移位寄存和接收移位寄存器,分别负责将发送数据并串转换和接收数据串并转换,从而实现数据在传输时,是一位一位的发送和接收。

总结

        比如发送一字节数据“ A”? “A”的ASCII值是0x41, 二进制就是01000001。事先双方约定好波特率、数据格式,如115200,数据位是8,停止位是1,不设校验位和流控

        初始电平为1(也就是高电平),发送方输出逻辑0(也就是低电平),并保持1位的时间,接收方检测到逻辑0,就知道对方准备发送数据了;

        发送方根据数据的bit 0-7设置引脚电平,并保持1位的时间,接收方读取引脚电平,得到bit 0-7;

        如果设置校验位还需要计算校验值,这里不设置可以省略这步

        最后发送方输出逻辑1,并保持1位的时间;接收方读取引脚电平, 直到数据传输结束

hal库代码

#define UARTx    USART1
#define UARTx_TX_PIN     GPIO_PIN_9
#define UARTx_RX_PIN     GPIO_PIN_10
#define UARTx_PORT       GPIOA

/*使能 USARTx 的输入输出引脚(GPIO)的时钟*/
#define USARTx_GPIO_CLK_EN() __HAL_RCC_GPIOA_CLK_ENABLE()
/*使能 USARTx 的时钟*/
#define USARTx_CLK_EN() __HAL_RCC_USART1_CLK_ENABLE()

#define USARTx_CLK_DIS() __HAL_RCC_USART1_CLK_DISABLE()

uint8_t buf[1]={0};

/*UART句柄*/
UART_HandleTypeDef g_uart1_hanle;

/*UART1初始化函数*/
void uart1_init(uint32_t baudrate)
{
    /*USART寄存器及地址:选择 USART1*/
    g_uart1_hanle.Instance = UARTx;
    /*配置波特率*/
    g_uart1_hanle.Init.BaudRate = baudrate;
    /*每次发送的字节长度:配置数据有效位为 8bit*/
    g_uart1_hanle.Init.WordLength = UART_WORDLENGTH_8B;
    /*每次发送后停止位长度: 一位停止位*/
    g_uart1_hanle.Init.StopBits = UART_STOPBITS_1;
    /*奇偶校验:不设校验位*/
    g_uart1_hanle.Init.Parity = UART_PARITY_NONE;
    /*流控设置:通常设置为None*/
    g_uart1_hanle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    /*收发模式:可收可发*/
    g_uart1_hanle.Init.Mode = UART_MODE_TX_RX;
    /*使用库函数初始化 USART1 的参数*/
    if(HAL_UART_Init(&g_uart1_hanle) != HAL_OK)
    {
        Error_Handler();
    }
    /*开启中断异步接收*/
    HAL_UART_Receive_IT(&g_uart1_hanle,(uint8_t *)buf,1);
}
/*重定义串口MSP回调函数,使能 USART1 的时钟,使能引脚时钟,并配置引脚的复用功能*/
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    /*保存GPIO参数设置;*/
    GPIO_InitTypeDef gpio_init_uart_struct = {0};
    /*判断是否想要的为USARTx(宏定义对应USART1)*/
    if(huart->Instance == UARTx)
    {
        /*使能 USART1 的时钟*/
        USARTx_CLK_EN();
        /*使能 USART1 的输入输出引脚的时钟*/
        USARTx_GPIO_CLK_EN();
        /*USART1 GPIO配置*/
        gpio_init_uart_struct.Pin  = UARTx_TX_PIN;/*选择 USART1 的 TX 引脚*/
        gpio_init_uart_struct.Mode = GPIO_MODE_AF_PP;/*配置为复用推挽功能*/
        gpio_init_uart_struct.Speed = GPIO_SPEED_FREQ_HIGH;/*引脚翻转速率快*/
        HAL_GPIO_Init(UARTx_PORT, &gpio_init_uart_struct);
        
        gpio_init_uart_struct.Pin = UARTx_RX_PIN;/*选择 RX 引脚*/
        gpio_init_uart_struct.Pull = GPIO_PULLUP;/*时序空闲时候为高电平,所以上拉*/
        gpio_init_uart_struct.Mode = GPIO_MODE_AF_INPUT;/*配置为复用输入功能*/
        HAL_GPIO_Init(UARTx_PORT, &gpio_init_uart_struct);
        /*中断优先级,使能中断*/
        HAL_NVIC_SetPriority(USART1_IRQn,0,0);
        HAL_NVIC_EnableIRQ(USART1_IRQn);
    }
}

/*中断服务函数*/
void USART1_IRQHandler(void)
{
    HAL_UART_IRQHandler(&g_uart1_hanle);
    HAL_UART_Receive_IT(&g_uart1_hanle,(uint8_t *)buf,1);
    
}
/*回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    printf("\r\ninput: %c\r\n",buf[0]);
}
//printf重定义
/*printf和scanf会分别调用“ fputc()”和“ fgetc()”,因此这里重写这两个函数
 *使用HAL提供的函数实现收发数据
- huart:UART外设的句柄,用于标识具体的UART外设。
- pData:一个指向待发送数据的缓冲区的指针。
- Size:待发送数据的长度,以字节为单位。
- Timeout:发送超时时间,如果数据在超时时间内没有发送完成,则函数会返回错误。
 */
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&g_uart1_hanle,(uint8_t*)&ch, 1, 10);
    return ch;
}
int fgetc(FILE *f)
{
    uint8_t ch = 0;
    HAL_UART_Receive(&g_uart1_hanle, (uint8_t*)&ch, 1, 10);
    return (int)ch;
}

标准库代码

// 串口1-USART1
#define  USART_BAUDRATE           115200
#define  USARTx                   USART1
#define  USARTx_CLK               RCC_APB2Periph_USART1
#define  USARTx_GPIO_CLK           (RCC_APB2Periph_GPIOA)
//apb时钟线使能函数
#define  USARTx_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  USARTx_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd

 USART GPIO 引脚宏定义
#define  USART_TX_GPIO_PIN        GPIO_Pin_9
#define  USART_RX_GPIO_PIN        GPIO_Pin_10
#define  USART_RX_GPIO_PORT       GPIOA
//中断相关
#define  USARTx_IRQ                USART1_IRQn
#define  USARTx_IRQHandler         USART1_IRQHandler

uint8_t rx_data[1]={0};

void uart_init(void)
{
    /*开启时钟*/
    USARTx_APBxClkCmd(USARTx_CLK,ENABLE);
    USARTx_GPIO_APBxClkCmd(USARTx_GPIO_CLK,ENABLE);

    /*初始化GPIO*/
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;/*发送引脚,输出模式:复用推挽输出*/
    GPIO_InitStruct.GPIO_Pin = USART_TX_GPIO_PIN;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(USART_RX_GPIO_PORT,&GPIO_InitStruct);
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;/*接收引脚,输入模式:上拉输入*/
    GPIO_InitStruct.GPIO_Pin = USART_RX_GPIO_PIN;
    GPIO_Init(USART_RX_GPIO_PORT,&GPIO_InitStruct);
    
    /*初始化uart接收*/
    USART_InitTypeDef USART_InitStruct;
    USART_InitStruct.USART_BaudRate = USART_BAUDRATE;/*波特率*/
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;/*不使用硬件流控*/
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;/*收发模式*/
    USART_InitStruct.USART_Parity = USART_Parity_No;/*不使用校验*/
    USART_InitStruct.USART_StopBits = USART_StopBits_1;/*一位停止位*/
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;/*8位字长*/
    USART_Init(USARTx,&USART_InitStruct);
    /*使能中断,开启EX标志位到NVIC的输出*/
    USART_ITConfig(USARTx, USART_IT_RXNE,ENABLE);
    /*配置NVIC*/
    /*分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    /*初始化NVIC的USART通道*/
    NVIC_InitTypeDef NVIC_InitStruct;
    NVIC_InitStruct.NVIC_IRQChannel = USARTx_IRQ;/*中断通道*/
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;/* 使能中断 */
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; /* 抢断优先级*/
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;  /* 子优先级 */
    NVIC_Init(&NVIC_InitStruct);
    /*开启USART*/
    USART_Cmd(USARTx,ENABLE);
}

/*中断服务函数*/
void USARTx_IRQHandler(void)
{
    if(USART_GetITStatus(USARTx,USART_IT_RXNE) == SET)
    {
        rx_data[0] = USART_ReceiveData(USARTx);
        printf("\r\ninput: %c\r\n",rx_data[0]);
        /*清除中断标志位*/
        USART_ClearITPendingBit(USARTx,USART_IT_RXNE);
    }
}

//重定位printf
/*printf和scanf会分别调用“ fputc()”和“ fgetc()”,因此这里重写这两个函数 
 *通过printf输出到串口打印,默认打印到屏幕,但是只支持一个串口使用*/
int fputc(int ch, FILE *f)
{
    uart_send_byte(ch);
    return ch;
}

int fgetc(FILE *f)
{
    return  USART_ReceiveData(USARTx);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值