玩转stm32 usart 串口功能 库函数 详解和DMA

发布者:JoyfulSpirit5最新更新时间:2015-12-14 来源: eefocus关键字:stm32  usart  串口功能  DMA 手机看文章 扫描二维码
随时随地手机看文章
数据传输时要从支持那些相关的标准?传输的速度?什么时候开始?什么时候结束?传输的内容?怎样防止通信出错?数据量大的时候怎么弄?硬件怎么连接出发,当然对于stm32还要熟悉库函数的功能

具起来rs232和485电平的区别硬件外围芯片,波特率(反映传一位的时间),起始位和停止位,数据宽度,校验,硬件流控制,相应连接电脑时的接口怎么样的。配置,使用函数,中断,查询并结合通信协议才算了解了串口使用。

以上是基础,当然stm很多相关复用功能,支持同步单向通信和半双工单线通信,支持局部互联网、智能卡协议和红外数据组织相关规范,以及调制解调器操作,运行多处理器通信。同时可以使用DMA方式进行高速数据通信。注意Print函数时间问题,尝试通过DMA解决。

特点:全双工,异步,分数波特率发生器好处是速度快,位数8或9为,可配置1或2停止位,Lin协议,可提供同步时钟功能。

 

硬件上

一般2个脚,RX和TX;同步模式需要SCLK时钟脚,红外IRDA需要irDA_RDI脚作为数据输入和irDA_TDO输出。

奇偶校验通过usart_cr1     pce位配置校验(发送生成奇偶位,接受时进行校验)

LIN局域互联网模式:通过设置USART_CR2中LINEN位配置,使用的时候需要外加专门的收发器才可以

同步模式:通过设置USART_CR2中CLKEN位配置

智能卡模式:  通过设置USART_CR3中SCEN位配置   

DMA、硬件流控制作专门研究。

 

中断有哪些事件?

发送数据寄存器空   发送完成  接受数据可读  奇偶校验错数据溢出  CTS标志 空闲标志 断开标志 噪声标志

遗憾是没有留有接受缓冲区,用查询容易数据丢失

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。

流程是时钟配置---管脚配置(如重映射,管脚模式)----串口配置----中断配置-----相应中断-----打开串口

上面是一些基础知识点,下面从实际运用来了解串口功能

比较简单些的应用吧:对usart进行初始化的工作

void COM1_Init( void)
{

//首先要初始化结构体:少不了对于的引脚,忘不了usart,更牵挂着中断的配置结构体,定义空间来被涂鸦

 GPIO_InitTypeDef GPIO_InitStructure;           
 USART_InitTypeDef USART_InitStructure;
 NVIC_InitTypeDef NVIC_InitStructure; 
 

//下面是对GPIO进行涂鸦
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

 

            
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                                       //选择管脚位
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                              //模式复用推挽输出
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
    
 
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;                                      //选择管脚位
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                           //模式为输入
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 USART_InitStructure.USART_BaudRate = 115200;                                    //波特率
 USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     //8数据位
 USART_InitStructure.USART_StopBits = USART_StopBits_1;                          //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(USART1, &USART_InitStructure);
 
 //使能串口中断,并设置优先级
 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = USART1_IRQn_Priority;
 NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
 NVIC_Init(&NVIC_InitStructure); //将结构体丢到配置函数,即写入到对应寄存器中

 
    //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
   
 //所有的工作都做好了,最后别忘了打开串口
 USART_Cmd(USART1, ENABLE);
}

 

//USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);下面是中断源

#define USART_IT_PE                          ((uint16_t)0x0028)
#define USART_IT_TXE                         ((uint16_t)0x0727)
#define USART_IT_TC                          ((uint16_t)0x0626)
#define USART_IT_RXNE                        ((uint16_t)0x0525)
#define USART_IT_IDLE                        ((uint16_t)0x0424)
#define USART_IT_LBD                         ((uint16_t)0x0846)
#define USART_IT_CTS                         ((uint16_t)0x096A)
#define USART_IT_ERR                         ((uint16_t)0x0060)
#define USART_IT_ORE                         ((uint16_t)0x0360)
#define USART_IT_NE                          ((uint16_t)0x0260)
#define USART_IT_FE                          ((uint16_t)0x0160)

 --------------------------------------------------------------------------------------

以上是初始化配置,下面还要构成最小的运用,就举例输出函数吧

void PrintUart1(const u8 *Str)
{
 while(*Str)
 {
  USART_SendData(USART1, (unsigned char)*Str++);
  while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
 }
}

发送字符是通过查询字符串的状态来来不断的发送下一个数据。

接受数据是通过中断来实现的,把接受的数据放入缓冲区,来实现。有包含协议以后细讲。

 

想玩电脑串口传输数据,通过printf()来直接在电脑窗口显示是不是很爽?在usart.c函数中加入

//不使用半主机模式
#if 1 //如果没有这段,则需要在target选项中选择使用USE microLIB
#pragma import(__use_no_semihosting)
struct __FILE 

 int handle; 
}; 
FILE __stdout; 

_sys_exit(int x) 

 x = x; 
}
#endif

int fputc(int ch, FILE *f)
{
  USART_SendData(USART1, (unsigned char)ch);
  while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
 return ch;
}

上面数据来源可以通过串口,通过usb,通过无线等等。。。

printf函数有个缺陷,就是花费的时间太多了(毫秒级),总不至于让CPU等几个毫秒就来显示串口吧,那再好的CPU也就费了,那肿么办?可以用DMA!!直接让其它硬件来传这些粗糙的工作。CPU玩重点的其它的活!

先接着讲好串口接受,再说这个DMA,在固件库里面有个文件是专门用来放中断处理函数的

里面有个函数
void USART1_IRQHandler(void)
{
 static u8 UartBuf[UART_BUF_LEN];//串口缓冲
 if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
 
  temp=USART_ReceiveData(USART1);

..............下面就是一些处理,可以用状态机模式来玩,直到填充好串口缓冲数据,并校验正确,不多说

  }
}[page]

上面是典型的中断处理函数,结合状态机功能就强大了。讲到操作系统还要进一步的学会运用。

---------------------------------------------------------------------------------------------

通过上面的学习相信对基本的串口操作有了比较深入的理解了,前面提到printf太慢,这里需要一些改进。

考虑到真正执行串口输出(用DMA其实在几毫秒后完成)比执行完pruntf(立即完成)这个时间点晚个几毫秒对实际应用来说没有任何影响,因此CPU可以在嘀嗒中断中指挥DMA模块完成串口输出的任务。(其实对系统还是有些影响的宏观讲,串口输出的数据其实很少,在大河中放入一杯水,几乎可以忽略不计的)
再解决怎么分配:
定义两个全局缓存区
其中一个缓存区以循环队列的形式组织,每次执行fputc时向其队尾加入一个元素。
另一个缓存区直接以数组的形式组织,作为DMA的源地址。
在嘀嗒中断中按下列顺序完成对DMA的操作
(1)判断循环队列是否为空,如果为空说明当前没有字符串需要通过串口输出直接跳至(6)
(2)判断DMA是否正在工作,如果DMA正在工作说明上次分配的任何没干完直接跳至(6)
(3)从循环队列出队N个字符到数组缓存
(4)告诉DMA本次需传输的字节数N
(5)命令DMA开始传输
(6)结束操作
补充:
1.N的确定方法:若循环队列中元素的个数大于或等于数组缓存区的长度(固定值),则将数组缓存区的
长度赋给N,若循环队列中元素的个数小于数组缓存区的长度则将循环队列元素的个数赋给N
2.循环队列开辟得越大,能缓存的字符串就越多,因此是越大越好.
3.数组缓存区并不是开辟的越大越好,这个值可以做如下的计算得出,假设波特率为115200嘀嗒中断
的周期为2毫秒则在这2毫秒时间内理论上最多可传115200*0.002/10=23个字节。因此把数组缓存区
的大小定到比23稍小一点即可,比如定为20.
代码可正常运行。经测试使用新方案printf一个包含了二十个字符的字符串只需要25微秒的CPU耗时,
而老方案则需要1.76毫秒的CPU耗时。从而可以放心的使用printf调试一些时序要求较高的函数了,
另外因为执行时间段从而printf被重入的概率大大减小。如果需要彻底防止printf被重入的话,可在调用printf之前关中断,在printf执行完之后开中断,代价也仅是可能发生的几十微秒的中断延时而已。

.........................................................................................

下面讲一讲我对DMA的理解

stm32   DMA有8通道,0---7

既然DMA传输的是数据,当然有数据的宽度了,这需要配置。另外还要有地址吧,从哪个地址开始传,还有传到哪个地址,需要配置。还有传输普通的就直接传完就好了,如果要高速不断循环传输,这也可以配置。还有从ROM直接快速的加载到RAM(反过来不可以)也可以的。

之上就是一些基本需要配置的工作

 DMA_DeInit(DMA1_Channel4); 

 上面这句是给DMA配置通道,根据ST提供的资料,STM3210Fx中DMA包含7个通道 

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&((USART_TypeDef *)USART1)->DR);
上面是设置外设地址

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) USART_DMA_BUF;

上面这句很显然是DMA要连接在Memory中变量的地址,上面设置存储器地址;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;

上面的这句是设置DMA的传输方向,就如前面我所说的,从存储器到外设,也可以从外设到存储器,:把DMA_DIR_PeripheralSRC改成DMA_DIR_PeripheralDST即可。

DMA_InitStructure.DMA_BufferSize = 0;

上面的这句是设置DMA在传输时缓冲区的长度,前面有定义过了buffer的起始地址:为了安全性和可靠性,一般需要给buffer定义一个储存片区,这个参数的单位有三种类型:Byte、HalfWord、word,我设置的2个half-word(见下面的设置);32位的MCU中1个half-word占16 bits。

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

上面的这句是设置DMA的外设递增模式,如果DMA选用的通道(CHx)有多个外设连接,需要使用外设递增模式:DMA_PeripheralInc_Enable;我的例子选用DMA_PeripheralInc_Disable

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

上面的这句是设置DMA的内存递增模式,DMA访问多个内存参数时,需要使用DMA_MemoryInc_Enable,当DMA只访问一个内存参数时,可设置成:DMA_MemoryInc_Disable。

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

上面的这句是设置DMA在访问时每次操作的数据长度。有三种数据长度类型,前面已经讲过了,这里不在叙述。

DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;

与上面雷同。在此不再说明。

DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;

上面的这句是设置DMA的传输模式:连续不断的循环模式,若只想访问一次后就不要访问了(或按指令操作来反问,也就是想要它访问的时候就访问,不要它访问的时候就停止),可以设置成通用模式:DMA_Mode_Normal

DMA_InitStructure.DMA_Priority = DMA_Priority_Low;

上面的这句是设置DMA的优先级别:可以分为4级:VeryHigh,High,Medium,Low.

DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

上面的这句是设置DMA的2个memory中的变量互相访问的

DMA_Init(DMA_Channel1,&DMA_InitStructure);

前面那些都是对DMA结构体成员的设置,在次再统一对DMA整个模块做一次初始化,使得DMA各成员与上面的参数一致。

DMA_Cmd(DMA_Channel1,ENABLE);

 

ok上面的配置工作完成了,相当于设定了一根管道通过DMA把缓冲区中要发送的数据发送到串口中。当然要使得DMA与usart1相连接,在usart1中还要把usart的DMA功能打开: USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);接着在程序中只需要监控,数据发送的状态,并适时的打开或关闭DMA,留着下一次的串口发送用。

函数重载的 printf()函数如下

int fputc(int ch, FILE *f)
{
  while(En_Usart_Queue(ch)==-1); 
 return ch;
 

起始就是往环形缓冲区中添加要串口打印的数据,这个动作是比较快的。因为cpu直接移动数据很快,而通过cpu来操作串口,等待收获反映,有个while(..)是比较慢得,故有几个毫秒的延时。现在好了,cpu ,通过printf只是移动数据到了缓冲区,缓冲区在一定的时候,cpu指挥dma来开始接下来的操作,然后dma操作,cpu接着做其他的事情,仅仅只要在下次空闲的时候来查一下dma有木有传输完成,或者有没有传输错误,并改变环形队列首位的位置,以及更改相应的状态就可以了。这样可以大大的节省很多的时间哦。
环形队列的结构,大家可以看一些算法,不是很难的。

下面是运行过程中,cpu操作查询的函数。

void DMA_USART_Handler(void){
 u16 num=0;
 s16 read;
 if(DMA_GetCurrDataCounter(DMA1_Channel4)==0){ //检查DMA是否完成传输任务
  DMA_Cmd(DMA1_Channel4, DISABLE);
  while((read=De_Usart_Queue())!=-1){
   USART_DMA_BUF[num]=read;
   num++; 
   if(num==USART_DMA_BUF_SIZE)
    break;
  }
  if(num>0){ 
   ((DMA_Channel_TypeDef *)DMA1_Channel4)->CNDTR = num;//数量寄存器清零
   DMA_Cmd(DMA1_Channel4, ENABLE);
  }
 }
}


关键字:stm32  usart  串口功能  DMA 引用地址:玩转stm32 usart 串口功能 库函数 详解和DMA

上一篇:STM32学习笔记(6.1):LCD的显示
下一篇:STM32 GPIO速度,模式等理解

推荐阅读最新更新时间:2024-03-16 14:40

STM32学习笔记—CAN总线收发数据常见问题分析
CAN,Controller Area Network(控制器局域网络),在汽车电子、工业控制领域的应用比较多,通常用于局域组网。 这是第9篇学习分享文章,《STM32学习笔记》之CAN总线收发数据常见问题分析。 CAN总线和UART、I2C、SPI总线最大的区别主要在于取消了传统的地址编码方式,理论上讲总线上的互联节点数不受限制,拥有强大的握手与出错管理及重发机制,具有很强的抗干扰能力。 STM32 CAN 基础内容 CAN网络中主要由CAN控制器和CAN收发器组成,大部分STM32内部都集成了CAN控制器,如果需要使用CAN功能,还需要在外部连接一个CAN收发器才能使用。 ▲ CAN 网络拓扑结构图 STM32内部
[单片机]
STM32通用定时器使用
STM32中一共有11个定时器,其中2个高级控制定时器,4个普通定时器和2个基本定时器,以及2个看门狗定时器和1个系统嘀嗒定时器。(TIM1和TIM8是能够产生3对PWM互补输出的高级登时其,常用于三相电机的驱动,时钟由APB2的输出产生;TIM2-TIM5是普通定时器;TIM6和TIM7是基本定时器,其时钟由APB1输出产生) 本实验要实现的功能是:用普通定时器TIM2每一秒发生一次更新事件,进入中断服务程序翻转LED1的状态。 预备知识: ① STM32通用定时器TIM2是16位自动重装载计数器。 ② 向上计数模式:从0开始计数,计到自动装载寄存器(TIMx_ARR)中的数值时,清0,依次循环。 需要弄清楚的两个问题: 1.
[单片机]
<font color='red'>STM32</font>通用定时器使用
STM32的NVIC_PriorityGroupConfig使用及优先级分组方式理解
STM32有43个channel的settable的中断源;AIRC(Application Interrupt and Reset Register)寄存器中有用于指定优先级的4 bits。这4个bits用于分配preemption优先级和sub优先级,在STM32的固件库中定义如下 #define NVIC_PriorityGroup_0 ((u32)0x700) #define NVIC_PriorityGroup_1 ((u32)0x600) #define NVIC_PriorityGroup_2 ((u32)0x500) #define NVIC_Priority
[单片机]
STM32总线架构讲解
01前文 本文主要进行学习总结,为他人提供参考和学习,也为了记录自己的学习知识进行总结,并且望有错误之处,请各位技术大佬们提出修正,互相进步,也便认识自己的不足之处,查漏补缺。 02前情提要 在说总线架构之前,要了解一些知识点,AMBA是什么、总线、矩阵的作用,了解了这些才能明白总线为何这样走,为何要有矩阵。 1、AMBA AMBA是ARM公司提出的一种开放性的SoC总线标准,现在已经广泛的应用于RISC的内核上了。 定义了一种多总线系统,包括系统总线和等级稍低的外设总线。 利用AMBA定义的一款总线架构: AHB (Advanced High-performance Bus) 高级高性能总线 ASB (Advanced Sy
[单片机]
<font color='red'>STM32</font>总线架构讲解
出货超40亿颗,STM32前景光明
意法半导体(以下简称ST)在MCU领域的地位是毋庸置疑的。 根据IHS 的数据统计显示,2018年,ST MCU(不含汽车和安全MCU)的全球出货量位居全球第二。其通用加汽车MCU在中国的出货量更是高居第一,领先于排名第二的的NXP。 STM 32 过去九年的的出货量 尤其是其STM32 MCU的出货,更是表现惊人。据IHS统计,过去四年,STM32的平均年复合增长率高达30%,去年公司的STM32出货量更是达到了12亿颗。统计2007年到现在的数据,公司的STM32出货量更是超过40亿片,服务的客户也超过四万家。能获得这样的成绩主要得益于公司过去多年来在产品和技术上的投入。 在日前于深圳举办的“STM32中
[单片机]
出货超40亿颗,<font color='red'>STM32</font>前景光明
11、STM32的ADC的原理与使用(内附代码)
一、ADC的原理及定义 Analog-to-Digital Converter的缩写。中文译名:模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。其实就是一个取样、量化、编码的一个过程。 典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。比如电量、光照传感器等常用。 二、模拟信号 模拟信号是指用连续变化的物理量表示的信息,其信号的幅度,或频率,或相位随时间作连续变化,如目前广播的声音信号,或图像信号等。 三、数字信号 数字信号指幅度的取值是离散的,幅值表示被限制在有限个数值之内。 二进制码就是一种数字信号。二进制码受噪声的影响小,易于有数字电路进行处理,所以得到了广泛
[单片机]
11、<font color='red'>STM32</font>的ADC的原理与使用(内附代码)
普通8位单片机和STM32单片机复位电路区别
普通8位单片机,如AT、STC等芯片厂商,单片机须使用高电平复位,复位后保持低电平 STM32单片机,使用低电平进行复位,NRST复位引脚在一般情况需保持高电平电路才能正常工作。 另外单片机上电时间是有要求的,所以因根据数据手册匹配指定的RC复位电路。
[单片机]
基于STM32的智能电机保护器设计
0 引言 电机是工业生产领域中最主要的驱动源,如何有效地监控电机的运行状态,保护电机回路,提高电机的运行时间,减少电机故障,对工厂整体电网的运行十分关键。 电动机保护装置有很多种,目前使用得比较普遍的还是基于金属片机械式的热继电器,它结构简单,在保护电动机过载方面具有反时限特性。但它的保护功能少,无断相保护,对电机发生通风不畅、扫膛、堵转、长期过载、频繁启动等故障也不能起保护作用。此外,热继电器还存在重复性能差、大电流过载或短路故障后不能再次使用、调整误差大、易受环境温度影响而误动或拒动、功耗大、耗材多、性能指标落后等缺陷。 为响应国家节能减排的要求,采用基于微控制器的电子式电机保护器替代现有热继电器,具有广大的
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved