【STM32】串口收发驱动Drv_Uart|学习笔记

一、什么事串口?

  大家常说串口,其实串口有很多种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);

	GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_UART4);
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_UART4);
	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);

	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);

	USART_ITConfig(UART4,USART_IT_RXNE,ENABLE);
	USART_Cmd(UART4,ENABLE);
}
void UART4_IRQHandler()
{
	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的半双工正常工作,简单来说就是收的时候不能发送发的时候不能接收,收发互斥。

原文地址:https://www.cnblogs.com/VergilYang/p/8902264.html

时间: 2024-09-30 17:33:14

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

Android深度探索(卷1)HAL与驱动开发学习笔记(5)

Android深度探索(卷1)HAL与驱动开发学习笔记(5) 第五章 搭建S3C6410的开发版的测试环境 1.  S3C6410 处理器概述   S3C6410是一个16/32位RISC微处理器,旨在提供一个具有成本效益.功耗低,性能高的应用处理器解决方案,像移动电话和一般的应用.它为2.5G 和 3G通信服务提供优化的H /W性能, S3C6410采用了64/32位内部总线架构.该64/32位内部总线结构由AXI.AHB 和APB总线组成.它还包括许多强大的硬件加速器,像视频处理,音频处理,

Android深度探索(卷1)HAL与驱动开发学习笔记(2)

Android深度探索(卷1)HAL与驱动开发学习笔记(2) 第二章搭建Android开发环境 书中介绍了两种JDK的安装方法, 方法一: 从官网下载JDK并进行配置,解压后在终端打开profile文件来设置PATH环境变量(# soure /etc/profile),打开profile文件后输入下面的内容 export PATH=.:developer/jdk6/bin:$PATH 保存profile文件以后,有两种方法可以重新加载profile文件. 1.# sourse  /etc/pro

Introduction the naive“scull” 《linux设备驱动》 学习笔记

Introduction the naive "scull" 首先,什么是scull? scull (Simple Character Utility for Loading Localities). scull is a char driver that acts on a memory area as though it were a device. 和第一个C程序Hello world一样,他什么都不能干,却能很好的阐释怎么一步步进阶的去写驱动 blog的最后,我会给出这对于sc

linux系统驱动基础学习笔记

Linux驱动: 角色:应用程序 API      操作系统      驱动       实际硬件 功能:1.对设备进行初始化和释放2.把数据从内核传送到硬件和从硬件读取数据3.检测和处理设备出现的错误 Linux驱动程序类型:字符设备:由文件系统管理    (通过设备文件访问)块设备:由文件系统管理网络设备:由协议栈管理      (通过socket访问) 查看系统设备文件ls -l /devcat /proc/devicescat /sys/power/state 属性:文件类型   主设备

Android深度探索(卷1)HAL与驱动开发学习笔记(7)

Android深度探索(卷1)HAL与驱动开发学习笔记(7) 第七章 控制发光二极管   LED驱动实现原理       2. 编写LED驱动 * 创建LED驱动的设备文件 ·使用cdev_init 函数初始化cdev ·指定设备号 ·使用cdev_add函数将字符设备添加到内核中字符设备组中 ·使用class_creat宏创建stuct class ·使用device_creat 创建设备文件 卸载LED驱动的设备文件 依次调用device_destory.class_destory.unre

Android深度探索(卷1)HAL与驱动开发学习笔记(4)

Android深度探索(卷1)HAL与驱动开发学习笔记(4) 第四章  源代码的下载与编译 一.源代码配置Android源代码下载环境 1.建一个用于存放下载脚本文件(repo)的目录 # mkdir ~/bin # PATH=~.bin:$PATH 2.下载repo脚本文件 # curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo #chmod a+x ~/bin/repo 3.创建用于存放Andro

Android深度探索(卷1)HAL与驱动开发学习笔记(6)

Android深度探索(卷1)HAL与驱动开发学习笔记(6) 第六章 第一个Linux驱动程序 统计单词个数   Linux系统将每一个驱动都映射成一个文件.这些文件称为设备文件或驱动文件,都保存在/dev目录中.这种设计理念使得与Linux驱动进行交互就像与普通文件进行交互一样容易.虽然C语言里没有事件的概念,但却有与事件类似的概念,这就是回调(c a l l b a c k)函数.因此,编写Lin u x驱动最重要的一步就是编写阴调函数,否则与设备文件交互的数据将无法得到处理. 6.1编写L

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

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

领域驱动设计学习笔记

最近学习了领域驱动设计,基本上熟悉了领域驱动的一些基本术语以及一些分析的方法,并结合了实际的开发架构.基本的概念是通过<领域驱动设计:软件核心复杂性应对之道>这本书来进行学习的,里面详细讲解了领域驱动的一些基本概念以及领域驱动的多个设计模式,如果想对领域驱动进行深入学习的话,这本书是一个不错的基础. 有了基本的概念之后,为了与实际的开发进行结合,我还阅读了<领域驱动设计C# 2008实现问题.设计.解决方案>.这本书作者通过实际的项目来展开讲解的,前面几章根据领域驱动的概念设计了领