一、什么事串口?

  大家常说串口,其实串口有很多种UART,SPI,IIC都是串口,一般大家口中的串口就是UART(Universal Asynchronous Receiver/Transmitter),STM32上集成了UART的控制器,所以我们通过简单的配置就可以实现UART通信的功能。当然光有控制器可以在单板间通信,但大部分的应用场景都是需要远距离的抗干扰传输,这时就需要做电平转换,,目前工业上常用的串口屏,串口透传zigbee,诸如此类的设备都会用到标准的串行接口,所以单板上一般会加一个收发器,来实现电平转换,常用的串行接口就是大家常说的232,485,422等。

对于STM32来说不同的接口控制方法基本类似(就两线制来说),485会多一条读写的控制引脚,因为它是半双工,不能同时读写。

二、怎样使用它?

1.串口外设使能,GPIO使能

  RCC_APB2PeriphClockCmd();

2.串口复位

  USART_DeInit();

3.GPIO模式设置

  GPIO_Init();

  GPIO_PinAFConfig();

4.串口参数初始化

  USART_Init();

5.开启中断并初始化NVIC  

  NVIC_Init();

  USART_ITConfig();

6.使能串口

  UART_Cmd();

7.编写中断处理函数

  USARTx_IRQHandler();

8.串口数据收发

  void USART_SendData();

  u8 USART_ReceiveData();

贴一个配置代码

这是串口控制器结构体

typedef struct Com_Manager
{
u8 Status;
u8 Send_Buf[256];
u16 TxByte_Counter;
u16 Stop_Byte;
u8 Recv_Buf[256];
u16 RxByte_Counter;
u16 OverTime_cnt;
}Com_Manager;

函数实现

void InitUart4(u32 bdr)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4,ENABLE);//IO时钟UART时钟不用说一定都要开启先 GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_UART4);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_UART4);//F2系列必须有这一句去开启IO的复用功能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE,&GPIO_InitStructure);
UART4_DIR_RX();//这里哦那个 USART_DeInit(UART4);
USART_InitStructure.USART_BaudRate = bdr;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
USART_Init(UART4,&USART_InitStructure);//UART配置 NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStructure);//NVIC配置 USART_ITConfig(UART4,USART_IT_RXNE,ENABLE);//可以查参考手册中关于串口中断的部分,RXNE指的是接收完成中断,每当接收到一个字节就进一次中断。
USART_Cmd(UART4,ENABLE);//启动UART
}
void UART4_IRQHandler()//UART USART要注意区分
{
if((USART_GetITStatus(UART4,USART_IT_RXNE))&&((Com[4].Status&0x06) == 0x00))
{
//建立接收
Com[4].Status |= COM_RECEIVING;
Com[4].Recv_Buf[Com[4].RxByte_Counter] = USART_ReceiveData(UART4);
Com[4].RxByte_Counter++;
Com[4].OverTime_cnt = 0;
if(Com[4].RxByte_Counter > 255)
{
Com[4].RxByte_Counter = 0;
Com[4].Status = COM_RECVFULL;//没想好怎么处理
}
}
}
u8 Drv_Uart_Async_Send(Com_Manager* port,u8* send_buf,u16 buf_size)
{
if((buf_size < 256)&&((port->Status&0x03) == 0x00))
{
//建立发送
port->Status |= COM_SENDING;
port->Stop_Byte = buf_size;
port->TxByte_Counter = 0;
memcpy(port->Send_Buf,send_buf,buf_size);
return 1;
}
else
{
//错误类型分类返回
return 0;
}
}
u16 Drv_Uart_Async_Recv(Com_Manager* port,u8 *recv_buf)
{
u16 counter_saver; if((port->Status&COM_RECVCOMPLETE) == COM_RECVCOMPLETE)
{
if(port->RxByte_Counter > 0)
{
counter_saver = port->RxByte_Counter;
memcpy(recv_buf,port->Recv_Buf,port->RxByte_Counter);
port->RxByte_Counter = 0;
port->OverTime_cnt = 0;
port->Status &= ~COM_RECVCOMPLETE;
return counter_saver;
}
else
{
return 0;
}
}
else
{
return 0;
}
}

发送这里用到了定时器,我用了50us来刷新是否有新建的发送任务及正在发送的任务,累计500us没有收到数据认为接收完成。

void Drv_Uart_50us_do()
{
//Com2循环发送处理************************************************************
if((Com[2].Status&COM_SENDING) == COM_SENDING)
{
if(USART_GetFlagStatus(USART2,USART_FLAG_TC) != RESET)
{
UART2_DIR_TX();
USART_SendData(USART2,Com[2].Send_Buf[Com[2].TxByte_Counter++]);
if(Com[2].TxByte_Counter > Com[2].Stop_Byte)
{
UART2_DIR_RX();//非常重要 坑了我一天 发送完必须复位RE 否则进不了接收中断
Com[2].Status &= ~COM_SENDING;
Com[2].TxByte_Counter = 0;
Com[2].Stop_Byte = 0;
}
}
}
//Com2接收超时处理
else if((Com[2].Status&COM_RECEIVING) == COM_RECEIVING)
{
Com[2].OverTime_cnt++;
if(Com[2].OverTime_cnt >= 10)
{
Com[2].Status |= COM_RECVCOMPLETE;
Com[2].Status &= ~COM_RECEIVING;//5ms仍未接收到数据认为接收完成
}
}
}

我这里用了一种状态处理机制,来保证485的半双工正常工作,简单来说就是收的时候不能发送发的时候不能接收,收发互斥。

但是这样做存在一个问题就是如果接受的数据没有及时处理,那么会造成一个死锁,接下来优化考虑用堆栈来接收数据,把接收到的数据压入栈,需要读的时候弹栈。

【STM32】串口收发驱动Drv_Uart|学习笔记的更多相关文章

  1. 驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址

    驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址 最近重新看了乾龙_Heron的<ARM 上电启动及 Uboot 代码分析>(下简称<代码分析>) ...

  2. 基于STM32的USB枚举过程学习笔记

    源:基于STM32的USB枚举过程学习笔记 基于STM32的USB枚举过程学习笔记(一) 基于STM32的USB枚举过程学习笔记(二) 基于STM32的USB枚举过程学习笔记(三) 基于STM32的U ...

  3. stm32串口收发导致的死机

    stm32串口收发导致的死机 很久以前有偶尔遇到过串口死机的情况,那是当时的我写出来的代码自己都觉得有问题,也就没注意.用了stm32做项目以后也就没遇到过了,今天做了个高压测试,每5ms定时发送一次 ...

  4. 驱动开发学习笔记. 0.04 linux 2.6 platform device register 平台设备注册 1/2 共2篇

    驱动开发读书笔记. 0.04  linux 2.6 platform device register 平台设备注册  1/2 共2篇下面这段摘自 linux源码里面的文档 : Documentatio ...

  5. 驱动开发学习笔记. 0.02 基于EASYARM-IMX283 烧写uboot和linux系统

    驱动开发读书笔记. 0.02 基于EASYARM-IMX283 怎么烧写自己裁剪的linux内核?(非所有arm9通用) 手上有一块tq2440,但是不知道什么原因,没有办法烧boot进norflas ...

  6. 驱动开发学习笔记. 0.06 嵌入式linux视频开发之预备知识

    驱动开发读书笔记. 0.06  嵌入式linux视频开发之预备知识 由于毕业设计选择了嵌入式linux视频开发相关的项目,于是找了相关的资料,下面是一下预备知识 UVC : UVC,全称为:USB v ...

  7. 驱动开发学习笔记. 0.05 linux 2.6 platform device register 平台设备注册 2/2 共2篇

    驱动开发读书笔记. 0.05 linux 2.6 platform device register 平台设备注册 2/2 共2篇 下面这段摘自 linux源码里面的文档 : 内核版本2.6.22Doc ...

  8. 驱动开发学习笔记. 0.01 配置arm-linux-gcc 交叉编译器

    驱动开发读书笔记. 0.01 配置arm-linux-gcc 交叉编译器 什么是gcc: 就像windows上的VS 工具,用来编译代码,具体请自己搜索相关资料 怎么用PC机的gcc 和 arm-li ...

  9. 基于STM32的USB枚举过程学习笔记(转)

    之前使用ST官方的库以及网络的资料,完成了使用USB HID类进行STM32和PC机的通讯.由于其他原因并没有深入的分析,虽然实现了功能,但是关于USB设备的枚举,以及具体的通讯方式都没有清晰的概念, ...

  10. stm32串口通信实验,一点笔记

    第一次深入学习stm32,花了好长时间才看懂代码(主要是C语言学习不够深入),又花了段时间自己敲了一遍,然后比对教程,了解了利用中断来串口通信的设置方法. 板子是探索版f407,本实验工程把正点原子库 ...

随机推荐

  1. 【C学习笔记】day5-1 完成猜数字游戏

    #include <stdio.h> #include <stdlib.h> int main() { int s = 0; srand((unsigned)time(NULL ...

  2. 【剑指Offer】【链表】复杂链表的复制

    题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head.(注意,输出结果中请不要返回参数中的节点引用,否则判 ...

  3. vue项目使用vue-amap调用高德地图api详细步骤

    想要的效果如下 : 高德地图 && 信息窗体 步骤一: 申请高德key 高德开放平台 | 高德地图API (amap.com) (可参考博客:   [996]如何申请高德地图用户Key ...

  4. redis底层数据结构之字典(dict)

    字典(dict) 字典又称为符号表或者关联数组.或映射(map),是一种用于保存键值对(key-value)的抽象数据结构 字典中的每个key都是唯一的,通过key对值来进行查找或修改,时间复杂度为 ...

  5. shell脚本定时任务转移项目日志

    1.之前同时项目部署在根目录,根目录磁盘空间40G,运行一年后日志占了18G的磁盘空间, 根目录只有几个G的磁盘空间,现在写shell脚本定时转移日志文件到挂载的磁盘目录下 2.编写shell脚本 # ...

  6. Unity组件Toggle详解

    1.首先先搭建UI(如下图) 2.如果实现单选的功能需要在Image上面挂载ToggleGroup脚本组件 2.2 选中三个Toggle把ToggleGroup拖到如下图位置即可 2.AllowSwi ...

  7. sync.Once

    保证在 Go 程序运行期间的某段代码只会执行一次 func main() { o := &sync.Once{} for i := 0; i < 10; i++ { o.Do(func( ...

  8. Java笔记_成员方法传参机制

    成员方法传参机制(非常重要) Parameter(参数).Method(方法).Exercise(练习) 基本数据类型的传参机制 看一下案例,将main中的值传递给类中对象 /** * @ClassN ...

  9. administrator 启用

    net user administrator /active:yes

  10. CSDN上书签迁移

    title: CSDN上书签迁移 date: 2020-11-01 16:34:30 img: /photos/2020.6.03_15/2020_06_11_cover.jpg summary: C ...