STM32
直播中

张华

7年用户 1388经验值
私信 关注
[问答]

处理器如何与外部设备通信?

处理器如何与外部设备通信

回帖(1)

李桂英

2021-12-13 09:37:17
基本知识

串口通讯 (Serial Communication)
串口中断其实也是依靠判断寄存器的状态进行中断服务的
通信的速率

Bitrate—比特率:每秒钟传输的二进制位数,单位为比特每秒(bit/s)
Baudrate—波特率:表示每秒钟传输的码元个数
一个二进制位表示一个码元 0V       ——      0  
两个二进制位表示一个码元
TTL电平转USB电平
处理器与外部设备通信的两种方式

![image.png](#align=left&display=inline&height=337&margin=[object Object]&name=image.png&originHeight=674&originWidth=813&size=306771&status=done&style=none&width=406.5)
并行通信




  • 传输原理:数据各个位同时传输。
  • 优点:速度快
  • 缺点:占用引脚资源多

串行通信




  • 传输原理:数据按位顺序传输。
  • 优点:占用引脚资源少
  • 缺点:速度相对较慢

![image.png](#align=left&display=inline&height=101&margin=[object Object]&name=image.png&originHeight=202&originWidth=1094&size=41026&status=done&style=none&width=547)
按照数据传送方向




  • 单工:数据传输只支持数据在一个方向上传输
  • 半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
  • 全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力

![image.png](#align=left&display=inline&height=250&margin=[object Object]&name=image.png&originHeight=499&originWidth=616&size=116084&status=done&style=none&width=308)
串行通信的通信方式




  • 同步通信:带时钟同步信号传输。SPI,IIC通信接口
  • 异步通信:不带时钟同步信号。UART(通用异步收发器),单总线
    | 通信标准 | 引脚说明 | 通信方式 | 通信方向 |
    | — | — | — | — |
    |   UART
    (通用异步收发器) | TXD:发送端
    RXD:接受端
    GND:公共地 | 异步通信 | 全双工 |
    |   单总线
    (1-wire) | DQ:发送/接受端 | 异步通信 | 半双工 |
    | SPI | SCK:同步时钟
    MISO:主机输入,从机输出
    MOSI:主机输出,从机输入 | 同步通信 | 全双工 |
    | I2C | SCL:同步时钟
    SDA:数据输入/输出端 | 同步通信 | 半双工 |
  • UART:通用异步收发器 USART:通用同步异步收发器
  • RXD:数据输入引脚。数据接受。 TXD:数据发送引脚。数据发送。

![image.png](#align=left&display=inline&height=119&margin=[object Object]&name=image.png&originHeight=237&originWidth=590&size=36281&status=done&style=none&width=295)
[tr]串口号RXDTXD[/tr]
1PA10PA9
2PA3PA2
3PB11PB10
4PC11PC10
5PD2PC12
![image.png](#align=left&display=inline&height=86&margin=[object Object]&name=image.png&originHeight=171&originWidth=555&size=86944&status=done&style=none&width=277.5)
![image.png](#align=left&display=inline&height=164&margin=[object Object]&name=image.png&originHeight=328&originWidth=686&size=102771&status=done&style=none&width=343)
物理层

RS-232 串口标准常用于计算机、路由与调制调解器(MODEN,俗称“猫”) 之 间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由) 和数据通讯设备 DCE(调制调解器)
![image.png](#align=left&display=inline&height=77&margin=[object Object]&name=image.png&originHeight=153&originWidth=663&size=69493&status=done&style=none&width=331.5)
![image.png](#align=left&display=inline&height=123&margin=[object Object]&name=image.png&originHeight=246&originWidth=425&size=24068&status=done&style=none&width=212.5)



  • 公头与母头
  • 使用逻辑 1 表示信号有效,逻辑 0 表示信号无效
  • 一般只使用 RXD、TXD 以及 GND 三条信号线,直 接传输数据信号

![image.png](#align=left&display=inline&height=54&margin=[object Object]&name=image.png&originHeight=107&originWidth=667&size=26511&status=done&style=none&width=333.5)
![image.png](#align=left&display=inline&height=117&margin=[object Object]&name=image.png&originHeight=234&originWidth=442&size=63502&status=done&style=none&width=221)
协议层

![image.png](#align=left&display=inline&height=56&margin=[object Object]&name=image.png&originHeight=112&originWidth=649&size=53210&status=done&style=none&width=324.5)
异步通讯中由于没有时钟信号 (如前面讲解的DB9接口中是 没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码。
在stm32中需要定义的参数



  • 起始位
  • 数据位(8位或者9位)
  • 奇偶校验位(第9位)
  • 停止位(1,15,2位)
  • 波特率设置

![image.png](#align=left&display=inline&height=88&margin=[object Object]&name=image.png&originHeight=175&originWidth=1012&size=29539&status=done&style=none&width=506)
虚线分开的每一格就是代表一个码元。
波特率
通讯的起始和停止信号
有效数据
数据校验:奇校验 (odd)、偶校验 (even)、0 校验 (space)、1 校验 (mark) 以及无校验 (noparity)。
![image.png](#align=left&display=inline&height=343&margin=[object Object]&name=image.png&originHeight=686&originWidth=680&size=172203&status=done&style=none&width=340)
![image.png](#align=left&display=inline&height=227&margin=[object Object]&name=image.png&originHeight=453&originWidth=552&size=66480&status=done&style=none&width=276)
  5.3 节对串口有过简单的介绍
在 4.4.1 章节端口复用功能已经讲解过
8.1.11 外设的 GPIO 配置
当然要初始化 NVIC 设置中 断优先级别,最后编写中断服务函数
串口初始化程序


  • 串口时钟使能,GPIO 时钟使能
  • 串口复位
  • GPIO端口模式设置
  • 串口参数初始化
  • 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
  • 使能串口
  • 编写中断处理函数,实现数据的接收和发送

USART_SR状态寄存器
USART_DR数据寄存器
USART_BRR波特率寄存器
波特率的计算
![image.png](#align=left&display=inline&height=139&margin=[object Object]&name=image.png&originHeight=277&originWidth=908&size=110979&status=done&style=none&width=454)
![image.png](#align=left&display=inline&height=142&margin=[object Object]&name=image.png&originHeight=283&originWidth=927&size=101376&status=done&style=none&width=463.5)
![image.png](#align=left&display=inline&height=166&margin=[object Object]&name=image.png&originHeight=332&originWidth=593&size=206292&status=done&style=none&width=296.5)
实现效果

1.STM32 通过串口和上位机的对话, STM32 在收到上位机发过来的字符串后,原原本本的返回给上位机。
2.电脑给单片机发命令,用于控制开发板上的灯。

相应的接口
代码理解
1.我都不知的正点原子是写了个啥玩野,还是去看看程序讲解视频吧
2.正点原子的是真**看不懂,好像有搞到寄存器中去了


main.c
main函数中没有什么,就是调用串口函数就好了


#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"


int main(void)
{       
  /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
  USART_Config();
  while(1)
        {       
               
        }       
}


stm32f10x_it.c
配置串口中断服务函数,就是把串口中断服务函数加入其中了


#include "stm32f10x_it.h"
#include "usart.h"
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  uint8_t ucTemp;
        if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
        {               
                ucTemp = USART_ReceiveData(DEBUG_USARTx);
    USART_SendData(DEBUG_USARTx,ucTemp);   
        }         
}

void NMI_Handler(void)
{
}

void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}



void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}

void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}

void SVC_Handler(void)
{
}

void DebugMon_Handler(void)
{
}

void PendSV_Handler(void)
{
}

void SysTick_Handler(void)
{
}


usar.h
基本的配置函数,一个发送数据,然后一个打印到上位机中,重点是重定向


#include "sys.h"
#include "usart.h"          


static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}


/**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;


        // 打开串口GPIO的时钟
        DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
       
        // 打开串口外设的时钟
        DEBUG_USART_APBxClkCmd(DEBUG_USART_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);
       
        // 配置串口的工作参数
        // 配置波特率
        USART_InitStructure.USART_BaudRate = DEBUG_USART_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(DEBUG_USARTx, &USART_InitStructure);
       
        // 串口中断优先级配置
        NVIC_Configuration();
       
        // 使能串口接收中断
        USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);       
       
        // 使能串口
        USART_Cmd(DEBUG_USARTx, ENABLE);            
}


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


/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
       
        for(i=0; i   {
            /* 发送一个字节数据到USART */
            Usart_SendByte(pUSARTx,array);       
  
  }
        /* 等待发送完成 */
        while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}


/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
        unsigned int k=0;
  do
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='');
  
  /* 等待发送完成 */
  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);       
}


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


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


                return (int)USART_ReceiveData(DEBUG_USARTx);
}


代码实践
出现的问题|经验之谈
1-如何发送16位的数据?
2-如何发送一个数组?
3-如何使用printf()?如何使用scanf()?
4-电脑端发送过来的数据是什么格式的?十进制?十六进制?字符?
5.在野火的代码中体会到了宏定义的好处了,方便移植和应用
6.中断服务函数不用声明
举报

更多回帖

×
20
完善资料,
赚取积分