STM32一文通(8) 串口通讯

一.原生串口通讯

  • 原生的串口通信主要是控制器跟串口的设备或者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信
  • 比如: GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块

二. 串口与PC通讯

  • USB转串口主要用于设备跟电脑通信
  • 电平转换芯片一般有CH340、PL2303、CP2102、FT232
  • 使用的时候电脑端需要安装电平转换芯片的驱动

三. RS232标准串口通讯

  • RS232标准串口主要用于工业设备直接通信
  • 电平转换芯片一般有MAX3232,SP3232

四. STM32串口

1. 内部结构

寄存器 功能
TX 数据发送
RX 数据接收
SCLK 时钟,仅同步通信时使用(不常用)
nRTS 发送请求(不常用)
nCTS 允许发送 (不常用)

2. 串口引脚分布

注意:串口一是APB2总线, 其他是APB1总线

3. 串口引脚重定义

参见复用重映射和调试I/O配置寄存器(AFIO_MAPR)

4. 串口寄存器

(1). 串口数据寄存器 USART_DR

USART_DR, 9位有效
USART_DR一个地址对应了两个物理内存。包含一个发送数据寄存器TDR和一个接收数据寄存器RDR。

(2). 串口数据寄存器 USART_CR1 USART_CR2 用来配置串口

(3). 串口波特率寄存器USART_BRR

五. 串口相关结构体

1. 串口初始化结构体 USART_InitTypeDef

typedef struct
{
  uint32_t USART_BaudRate;            /*!< This member configures the USART communication baud rate.
                                           The baud rate is computed using the following formula:
                                            - IntegerDivider = ((PCLKx) / (16 * (USART_InitStruct->USART_BaudRate)))
                                            - FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5 */

  uint16_t USART_WordLength;          /*!< Specifies the number of data bits transmitted or received in a frame.
                                           This parameter can be a value of @ref USART_Word_Length */

  uint16_t USART_StopBits;            /*!< Specifies the number of stop bits transmitted.
                                           This parameter can be a value of @ref USART_Stop_Bits */

  uint16_t USART_Parity;              /*!< Specifies the parity mode.
                                           This parameter can be a value of @ref USART_Parity
                                           @note When parity is enabled, the computed parity is inserted
                                                 at the MSB position of the transmitted data (9th bit when
                                                 the word length is set to 9 data bits; 8th bit when the
                                                 word length is set to 8 data bits). */
 
  uint16_t USART_Mode;                /*!< Specifies wether the Receive or Transmit mode is enabled or disabled.
                                           This parameter can be a value of @ref USART_Mode */

  uint16_t USART_HardwareFlowControl; /*!< Specifies wether the hardware flow control mode is enabled
                                           or disabled.
                                           This parameter can be a value of @ref USART_Hardware_Flow_Control */
} USART_InitTypeDef;
  • USART_BaudRate: 设置波特率,如9600,115200等
  • USART_WordLength: 设置数据长度。具体值:USART_WordLength_8b 或 USART_WordLength_9b
  • USART_StopBits: 设置停止位大小
  • USART_Parity: 奇偶校验
  • USART_Mode: 设置收发使能

发送接收都使能

USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //发送接收都使能

2.同步时钟初始化结构体 (用的少)

typedef struct
{

  uint16_t USART_Clock;   /*!< Specifies whether the USART clock is enabled or disabled.
                               This parameter can be a value of @ref USART_Clock */

  uint16_t USART_CPOL;    /*!< Specifies the steady state value of the serial clock.
                               This parameter can be a value of @ref USART_Clock_Polarity */

  uint16_t USART_CPHA;    /*!< Specifies the clock transition on which the bit capture is made.
                               This parameter can be a value of @ref USART_Clock_Phase */

  uint16_t USART_LastBit; /*!< Specifies whether the clock pulse corresponding to the last transmitted
                               data bit (MSB) has to be output on the SCLK pin in synchronous mode.
                               This parameter can be a value of @ref USART_Last_Bit */
} USART_ClockInitTypeDef;

六. 串口相关库函数

1. 串口初始化函数 USART_Init()

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)

示例:

USART_InitStructure.USART_BaudRate=115200; //设置波特率
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(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);

2. 中断配置函数 USART_ITConfig()

void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)

例:使能接受完毕中断USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

3. 串口使能函数 USART_Cmd

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)

4. 数据发送函数 USART_SendData()

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)

5. 数据接收函数 USART_ReceiveData

uint16_t USART_ReceiveData(USART_TypeDef* USARTx)

6.中断状态位获取函数 USART_GetITStatus

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)

7. 编程方法

例子:

  • 完整的串口发送, 可以printf, 可以发送 8位字 16位半字节 字符串等

  • 中断接收功能 , 当有外部数据输入时会产生中断, 中断后将得到的 再发送出去,同时点亮RGB

  • 可以控制RGB灯的亮灭, PC端输入:0,1,2可以点亮RGB~

psb_usart.h文件(这个文件将5个串口都定义好了, 通过选择编译来确定串口)

#ifndef __BSP_USART_H
#define __BSP_USART_H

#include "stm32f10x.h"
#include <stdio.h>

//用哪个串口就把哪个置1
#define DEBUG_USART1     1
#define DEBUG_USART2     0
#define DEBUG_USART3     0
#define DEBUG_USART4     0
#define DEBUG_USART5     0

#if DEBUG_USART1
// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

#elif DEBUG_USART2
//串口2-USART2
#define  DEBUG_USARTx                   USART2
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

#define  DEBUG_USART_IRQ                USART2_IRQn
#define  DEBUG_USART_IRQHandler         USART2_IRQHandler

#elif DEBUG_USART3
//串口3-USART3
#define  DEBUG_USARTx                   USART3
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOB)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOB   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
#define  DEBUG_USART_RX_GPIO_PORT       GPIOB
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

#define  DEBUG_USART_IRQ                USART3_IRQn
#define  DEBUG_USART_IRQHandler         USART3_IRQHandler

#elif DEBUG_USART4
//串口4-UART4
#define  DEBUG_USARTx                   UART4
#define  DEBUG_USART_CLK                RCC_APB1Periph_UART4
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

#define  DEBUG_USART_IRQ                UART4_IRQn
#define  DEBUG_USART_IRQHandler         UART4_IRQHandler

#elif DEBUG_USART5
//串口5-UART5
#define  DEBUG_USARTx                   UART5
#define  DEBUG_USART_CLK                RCC_APB1Periph_UART5
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
#define  DEBUG_USART_RX_GPIO_PORT       GPIOD
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2

#define  DEBUG_USART_IRQ                UART5_IRQn
#define  DEBUG_USART_IRQHandler         UART5_IRQHandler

#endif

void USART_Config(void);
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data);
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num);
void Usart_SendStr(USART_TypeDef* pUSARTx, char *str);
#endif  /* __BSP_USART_H */

psb_usart.c

#include "psb_usart.h"
static void NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStruct;//中断初始化结构体
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//先设组
    NVIC_InitStruct.NVIC_IRQChannel = DEBUG_USART_IRQ;//中断源
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//主优先级
    NVIC_InitStruct.NVIC_IRQChannelSubPriority=7;//从优先级
    NVIC_InitStruct.NVIC_IRQChannelCmd= ENABLE; //中断使能
    
    NVIC_Init(&NVIC_InitStruct); //中断初始化
}
void USART_Config(void)
{
    //0.变量定义区
    GPIO_InitTypeDef GPIO_InitStructure; //创建一个GPIO_InitTypeDef类型的数据
    USART_InitTypeDef USART_InitStruct;//串口初始化结构体
    
    //1.配置GPIO
    DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE); //开启时钟
    
    // 将USART Tx的GPIO配置为推挽复用模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
    GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
    
    //2.初始化串口
    DEBUG_USART_APBxClkCmd(RCC_APB2Periph_USART1,ENABLE);
    
    USART_InitStruct.USART_BaudRate = DEBUG_USART_BAUDRATE; //波特率
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;//8位数据为
    USART_InitStruct.USART_StopBits = USART_StopBits_1; //停止位1
    USART_InitStruct.USART_Parity = USART_Parity_No;  //无奇偶校验
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //发送接收都使能
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流 
    
    USART_Init(USART1, &USART_InitStruct);//初始化串口
    
    //3.中断配置
    NVIC_Config();
    // 使能串口接收中断
    USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);    
    
    //4. 使能串口
    USART_Cmd(DEBUG_USARTx, ENABLE);
    
}
/*----------------------------------------------------------------------*/
//5. 编写发送函数

//发送一个字节
void Usart_SendByte(USART_TypeDef* USARTx,uint8_t data)
{
    USART_SendData(USARTx, data);
    while(USART_GetFlagStatus(USARTx, USART_FLAG_TXE)==RESET);
}
//发送两个字节
void Usart_SendHalfWord(USART_TypeDef* USARTx,uint16_t data)
{
    uint8_t tempH, tempL;
    tempL =  data & 0xFF;
    tempH =  (data>>8) & 0xFF;
    Usart_SendByte(USARTx,tempH);
    Usart_SendByte(USARTx,tempL);
}
//发送一个数组
void Usart_SendArray(USART_TypeDef* USARTx,uint8_t *array, uint8_t num)
{
    uint8_t i;
    for(i=0;i<num;i++)
    {
        Usart_SendByte(USARTx,*array++);
    }
    while(USART_GetFlagStatus(USARTx, USART_FLAG_TC)==RESET);
}
//发送一个字符串
void Usart_SendStr(USART_TypeDef* pUSARTx, char *str)
{
    uint8_t i=0;
    do
  {
        Usart_SendByte(pUSARTx, *(str+i));
        i++;
    }while(*(str+i) != '\0');
    while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
}

//重定向c库函数printf到串口,重定向后可使用printf函数和putchar函数
int fputc(int ch, FILE *f)
{
        /* 发送一个字节数据到串口 */
        USART_SendData(DEBUG_USARTx, (uint8_t) ch);
        
        /* 等待发送完毕 */
        while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);     
    
        return (ch);
}

/*----------------------------------------------------------------------*/
//6. 编写接收函数
uint16_t Rev_Byte(void)
{
    return USART_ReceiveData(DEBUG_USARTx);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
        /* 等待串口输入数据 */
        while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

        return (int)USART_ReceiveData(DEBUG_USARTx);
}
/*----------------------------------------------------------------------*/

中断部分

#include "stm32f10x_it.h"
#include "psb_usart.h"
#include "led.h"

//省略未修改内容

void DEBUG_USART_IRQHandler(void)
{
  uint8_t ucTemp;
    if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
    {       
        ucTemp = getchar();
    USART_SendData(DEBUG_USARTx,ucTemp); 
            switch(ucTemp)
            {
                case '0':
                    LED_R(ON);
                    LED_G(OFF);
                    LED_B(OFF);
                break;
                case '1':
                    LED_R(OFF);
                    LED_G(ON);
                    LED_B(OFF);
                break;
                case '2':
                    LED_R(OFF);
                    LED_G(OFF);
                    LED_B(ON);
                break;
                default:
                    LED_R(OFF);
                    LED_G(OFF);
                    LED_B(OFF);
                    break;
            }
    }
}

main.c随便写

#include "stm32f10x.h"
#include "led.h"
#include "psb_usart.h"

int main(void)
{
    uint8_t a[11]= {0,1,2,3,4,5,6,7,8,9,10};
    LED_GPIO_Config();
    USART_Config();
    LED_B(OFF);
    LED_R(OFF);
    LED_G(OFF);
    
    //Usart_SendByte(DEBUG_USARTx,0x08);
    //Usart_SendHalfWord(DEBUG_USARTx,0xabcd);
    //Usart_SendArray(DEBUG_USARTx, a,11);
    //Usart_SendStr(DEBUG_USARTx,"滚滚长江东逝水\n");
    //printf("浪花淘尽英雄~\n");
    printf("PC端输入:0,1,2可以点亮RGB~\n");
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355