您的位置 首页 数字

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

…………..下面便是一些处理,能够用状况机方式来玩,直到填充好串口缓冲数据,并校验正确,不多说

}
}

上面是典型的中止处理函数,结合状况机功用就强壮了。讲到操作体系还要进一步的学会运用。

———————————————————————————————

经过上面的学习信任对根本的串口操作有了比较深化的了解了,前面说到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);
}
}
}

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/zhishi/shuzi/259916.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部