STM32 串口功能 库函数 详解和DMA 串口高级运用(转载)

发布者:脑力激荡最新更新时间:2016-06-14 来源: eefocus关键字:STM32  串口功能  库函数  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通道,07

既然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);
  }
 }
}

 

 

 

 

 

一. DMA原理:

DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依于 CPU 的大量 中断 负载。否则,CPU 需要从 来源 把每一片段的资料到 暂存器,然后把它们再次写回到新的地方。在这个时间中,CPU 对于其他的工作来说就无法使用。

DMA 传输将数据从一个地址空间到另外一个地址空间。当 CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器 来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。

二.STM32使用DMA

1.DMA的设置:

要配置的有DMA传输通道选择,传输的成员和方向、普通模式还是循环模式等等。

void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    //DMA设置:
    //设置DMA源:内存地址&串口数据寄存器地址
    //方向:内存-->外设
    //每次传输位:8bit
    //传输大小DMA_BufferSize=SENDBUFF_SIZE
    //地址自增模式:外设地址不增,内存地址自增1
    //DMA模式:一次传输,非循环
    //优先级:中
    DMA_DeInit(DMA1_Channel4);//串口1的DMA传输通道是通道4
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff; //DMA访问的数据地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//外设作为DMA的目的端
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;//传输数据大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不增加
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址自增1
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    //DMA_Mode_Normal(只传送一次), DMA_Mode_Circular (循环传送)
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//(DMA传送优先级为中等)
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);
}
注:
1、传输通道:通过查表,串口1的发送对应的是DMA的通道4,所以此处选择通道4.
2、DMA传输方式:
(1) DMA_Mode_Normal,正常模式,当一次DMA数据传输完后,停止DMA传送,对于上例而言,就是DMA_PeripheralDataSize_Byte个字节的传送完成后,就停止传送。
(2) DMA_Mode_Circular
循环模式,当传输完一次后,重新接着传送,永不停息。
 
2、外设的DMA方式设置
将串口1设置成DMA模式:
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
 
3、待传输数据的定义和初始化
#define SENDBUFF_SIZE   10240
vu8 SendBuff[SENDBUFF_SIZE];
    for(i=0;i     {
        SendBuff[i] = i+'0';
    }
4、开始DMA传输(使能对应的DMA通道)
DMA_Cmd(DMA1_Channel4, ENABLE);
 
5、DMA传输的完成
 while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET)
 {
       LED_1_REV;      //LED改变亮灭
       Delay();        //浪费时间
 }
当传输完成后,就会跳出上面的死循环。
 
下面是九九的一个例程,测试过,可以运行!
 

#include "stm32f10x_lib.h"
#include "stdio.h"


#define USART1_DR_Base  0x40013804


#define SENDBUFF_SIZE   10240
vu8 SendBuff[SENDBUFF_SIZE];
vu8 RecvBuff[10];
vu8 recv_ptr;

void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
void DMA_Configuration(void);
void USART1_Configuration(void);
int fputc(int ch, FILE *f);
void Delay(void);


int main(void)
{
    u16 i;
#ifdef DEBUG
    debug();
#endif
    recv_ptr = 0;
    
    RCC_Configuration();
    GPIO_Configuration();
    NVIC_Configuration();
    DMA_Configuration();
    USART1_Configuration();
    
    printf("\r\nSystem Start...\r\n");
    printf("Initialling SendBuff... \r\n");
    for(i=0;i     {
        SendBuff[i] = i+'0';
    }
    printf("Initial success!\r\nWaiting for transmission...\r\n");
    //发送去数据已经准备好,按下按键即开始传输
    while(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3));
    
    printf("Start DMA transmission!\r\n");
    
    //这里是开始DMA传输前的一些准备工作,将USART1模块设置成DMA方式工作
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    //开始一次DMA传输!
    DMA_Cmd(DMA1_Channel4, ENABLE);
    
    //等待DMA传输完成,此时我们来做另外一些事,点灯
    //实际应用中,传输数据期间,可以执行另外的任务
    while(DMA_GetFlagStatus(DMA1_FLAG_TC4) == RESET)
    {
        Delay();        //浪费时间
    }
    //DMA传输结束后,自动关闭了DMA通道,而无需手动关闭
    //下面的语句被注释
    //DMA_Cmd(DMA1_Channel4, DISABLE);
    
    printf("\r\nDMA transmission successful!\r\n");
    
   
    while (1)
    {
    }
}

int fputc(int ch, FILE *f)
{
    //USART_SendData(USART1, (u8) ch);
    USART1->DR = (u8) ch;
    
   
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {
    }
    return ch;
}

void Delay(void)
{
    u32 i;
    for(i=0;i<0xF0;i++);
    return;
}

void RCC_Configuration(void)
{
    ErrorStatus HSEStartUpStatus;
    //使能外部晶振
    RCC_HSEConfig(RCC_HSE_ON);
    //等待外部晶振稳定
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
    //如果外部晶振启动成功,则进行下一步操作
    if(HSEStartUpStatus==SUCCESS)
    {
        //设置HCLK(AHB时钟)=SYSCLK
        RCC_HCLKConfig(RCC_SYSCLK_Div1);
        //PCLK1(APB1) = HCLK/2
        RCC_PCLK1Config(RCC_HCLK_Div2);
        //PCLK2(APB2) = HCLK
        RCC_PCLK2Config(RCC_HCLK_Div1);
        //FLASH时序控制
        //推荐值:SYSCLK = 0~24MHz   Latency=0
        //        SYSCLK = 24~48MHz  Latency=1
        //        SYSCLK = 48~72MHz  Latency=2
        FLASH_SetLatency(FLASH_Latency_2);
        //开启FLASH预取指功能
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
        //PLL设置 SYSCLK/1 * 9 = 8*1*9 = 72MHz
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
        //启动PLL
        RCC_PLLCmd(ENABLE);
        //等待PLL稳定
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        //系统时钟SYSCLK来自PLL输出
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        //切换时钟后等待系统时钟稳定
        while(RCC_GetSYSCLKSource()!=0x08);

       
    }
    //下面是给各模块开启时钟
    //启动GPIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA RCC_APB2Periph_GPIOB \
                           RCC_APB2Periph_GPIOC RCC_APB2Periph_GPIOD,\
                           ENABLE);
    //启动AFIO
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    //启动USART1
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    //启动DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
}
 

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    //PC口4567脚设置GPIO输出,推挽 2M
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 GPIO_Pin_5 GPIO_Pin_6 GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    //KEY2 KEY3 JOYKEY
    //位于PD口的3 4 11-15脚,使能设置为输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 GPIO_Pin_4 GPIO_Pin_11 GPIO_Pin_12 \
        GPIO_Pin_13 GPIO_Pin_14 GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);
    //USART1_TX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //USART1_RX
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}
 

void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
#ifdef  VECT_TAB_RAM
    // Set the Vector Table base location at 0x20
    NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else 
    // Set the Vector Table base location at 0x08
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
    //设置NVIC优先级分组为Group2:0-3抢占式优先级,0-3的响应式优先级
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    //串口接收中断打开    
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


void USART1_Configuration(void)
{
    USART_InitTypeDef USART_InitStructure;
    
    USART_InitStructure.USART_BaudRate = 9600;
    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_Tx USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);
    
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    USART_Cmd(USART1, ENABLE);
}

void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    //DMA设置:
    //设置DMA源:内存地址&串口数据寄存器地址
    //方向:内存-->外设
    //每次传输位:8bit
    //传输大小DMA_BufferSize=SENDBUFF_SIZE
    //地址自增模式:外设地址不增,内存地址自增1
    //DMA模式:一次传输,非循环
    //优先级:中
    DMA_DeInit(DMA1_Channel4);//串口1的DMA传输通道是通道4
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//外设作为DMA的目的端
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;//传输大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不增加
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址自增1
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//DMA_Mode_Normal(只传送一次), DMA_Mode_Circular (不停地传送)
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//(DMA传送优先级为中等)
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);
}

关键字:STM32  串口功能  库函数  DMA 引用地址:STM32 串口功能 库函数 详解和DMA 串口高级运用(转载)

上一篇:STM32缺陷之一:串口中断标志位缺陷
下一篇:STM32水温控制系统硬件详解

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

STM32学习——两轮平衡小车之硬件设计
这篇文先写最开始的电路设计,之后会更新后续的代码编写和小车调试。 电路设计的基本过程一般是这样的:需求分析——元件选型——原理图设计——PCB设计——焊接调试。 需求分析:在这里,主要是确定小车需要哪些模块、外设或接口。首先,stm32最小系统是必须的,这是小车控制的核心。然后小车的两个轮子需要两个H桥驱动和编码器接口。需要陀螺仪感知小车的姿态(包括倾角,转向角,角速度等)。需要一些调试和指示用的外设(蜂鸣器,LED等)。需要电源电路为系统供电。需要电池电压采集电路来实时采集电池电压,做低压报警,防止电池过放。需要下载接口和调试用的串口。基本就是根据自己想要实现功能,然后确定需要那些部分的电路。 元件选型:为需要的各个
[单片机]
<font color='red'>STM32</font>学习——两轮平衡小车之硬件设计
STM32关于USB的相关寄存器
这里要贴上STM32 USB相关的寄存器的具体设置,对学习STM32的USB很有帮助。对于涉及到寄存部分的代码,参考这些寄存器的说明,就可以差不多都懂了。由于网页贴不上表格,所以就截屏成图片贴上来,如果看不清楚,就下载图片去看 1.编程好习惯之变量定义: 定义变量时总是按变量类型所占空间大小来排序是最好的! 如果是只有某个函数使用些变量,而且你又需要在函数让函数退出时不被销毁,那么就使用static吧 2.编程好习惯之函数定义: 如果我们定义的C函数仅在此C文件中被调用那么加上static吧(适用于多文件编程) 如果我们想躲避C函数参数的类型检查,那 么K&R的函数定义声明方式还
[单片机]
<font color='red'>STM32</font>关于USB的相关寄存器
基于STM32嵌入式系统的智能控制网络终端设计
  智能网络终端是实现智能化管理的嵌入式终端设备。通常具有安全门禁系统和自动化控制的基本功能。本文重点介绍基于 STM32 的智能嵌入式终端的网络控制功能的设计和实现。    1智能网络通信实现方案   将 嵌入式系统 与Internet连接起来实现远距离信息获取和控制功能的本质是 嵌入式系统 本身能够实现TCP/IP网络通信协议。该协议的解决方案总体上可分为两大类。第一类是直接在嵌入式设备上实现TCP/IP,使之直接连上Internet。这实质上是由MCU及内部固化TCP/IP协议的芯片组成应用系统的核心,MCU可以直接拨号上网,这种方法的硬件电路相对简单,也不需要中间环节的支持。但是由于使用了TCP/IP协议芯片,因此需要大
[单片机]
基于<font color='red'>STM32</font>嵌入式系统的智能控制网络终端设计
STM32的CAN应用指南
CAN基本知识: CAN协议的特点: 1、 多主机控制。在总线空闲时,所有单元都可以发送消息,若两个以上同时开始发送消息,根据标识符来决定优先级。优先级高的先发送。 2、 系统的若软性。与总线相连的单元没有类似地址的信息。因此在总线上增加单元时,应用层程序基本不需要改变。 3、 通信速度快,通信距离远。最高1Mbps(距离小于40M),最远可达10Km(速率低于5Kbps)。 4、 具有错误检测、错误通知和错误恢复功能。所有单元都可以检测错误,检测出错误的单元会立即同时通知其他所有单元,正在发送消息的单元一旦检测出错误,会强制结束当前的发送。前置结束发送的单元会不断反复地重新发送该消息直到发送成功。 5、 故障封
[单片机]
STM32精确延时函数
#include stm32f10x_systick.h //使用SysTick的普通计数模式对延迟进行管理 //包括delay_us,delay_ms //修正了中断中调用出现死循环的错误 //防止延时不准确,采用do while结构! static u8 fac_us=0;//us延时倍乘数 static u16 fac_ms=0;//ms延时倍乘数 //初始化延迟函数 void delay_init(u8 SYSCLK) // 系统时钟是72M 即SYSCLK 取72 ,其他类推 { SysTick- CTRL&=0xfffffffb;//选择内部时
[单片机]
STM32的SPI接口、cubeMX软件配置SPI接口和分析SPI相关代码
本文主要介绍STM32的SPI接口、cubeMX软件配置SPI接口和分析SPI相关代码。 STM32之SPI简介: SPI协议【Serial Peripheral Interface】 串行外围设备接口,是一种高速全双工的通信总线。主要用在MCU与FLASHADCLCD等模块之间的通信。 SPI信号线 SPI 共包含 4 条总线。 SS(Slave Select):片选信号线,当有多个SPI 设备与 MCU 相连时,每个设备的这个片选信号线是与 MCU 单独的引脚相连的,而其他的 SCK、MOSI、MISO 线则为多个设备并联到相同的 SPI 总线上,低电平有效。 SCK (Serial Clock):时钟信号线,由主通信设备
[单片机]
<font color='red'>STM32</font>的SPI接口、cubeMX软件配置SPI接口和分析SPI相关代码
【stm32f0】stm32 总中断的打开与关闭
问题: 对于基于ARM Cortex M0内核的STM32芯片各类应用开发时,有的时候需要进行总的中断的开、关处理。那就究竟有没有开、关总的中断的函数或者指令呢? 回答: 随着Corte Mn各种内核的MCU的芯片越来越多和相关编译工具的升级换代,编译工具在有关内核指令操作的文档安排以及函数书写等方面可能发生了细微的变化。所以即使用过STM32 F1系列产品的工程师,在使用晚推出的STM32 F0芯片开发时,发现那些跟内核操作有关的指令或函数不知道哪里去找了。以STM32各系列的标准固件库为例,与内核相关的指令及函数都可以在...librariescmsis...后面目录的相关文件里找到。于不同系列的标准库中对应的子目录以及相
[单片机]
STM32电源管理系统浅谈
电源对电子设备的重要性不言而喻,它是保证系统稳定运行的基础,而保证系统能稳定运行后,又有低功耗的要求。在很多应用场合中都对电子设备的功耗要求非常苛刻,如某些传感器信息采集设备,仅靠小型的电池提供电源,要求工作长达数年之久,且期间不需要任何维护;由于智慧穿戴设备的小型化要求,电池体积不能太大导致容量也比较小,所以也很有必要从控制功耗入手,提高设备的续行时间。 STM32的电源管理系统主要分为: 1、备份域 2、调压器供电电路 3、ADC电源电路 备份域电路 STM32的备份域包括LSE振荡器、RTC、备份寄存器及备份SRAM这些器件,这部分的电路可以通过STM32的VBAT引脚获取供电电源,在实际应用中一般会使用3V的钮扣电
[单片机]
<font color='red'>STM32</font>电源管理系统浅谈
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
  • ARM裸机篇--按键中断
    先看看GPOI的输入实验:按键电路图:GPF1管教的功能:EINT1要使用GPF1作为EINT1的功能时,只要将GPFCON的3:2位配置成10就可以了!GPF1先配 ...
  • 网上下的--ARM入门笔记
    简单的介绍打今天起菜鸟的ARM笔记算是开张了,也算给我的这些笔记找个存的地方。为什么要发布出来?也许是大家感兴趣的,其实这些笔记之所 ...
  • 学习ARM开发(23)
    三个任务准备与运行结果下来看看创建任务和任运的栈空间怎么样的,以及运行输出。Made in china by UCSDN(caijunsheng)Lichee 1 0 0 ...
  • 学习ARM开发(22)
    关闭中断与打开中断中断是一种高效的对话机制,但有时并不想程序运行的过程中中断运行,比如正在打印东西,但程序突然中断了,又让另外一个 ...
  • 学习ARM开发(21)
    先要声明任务指针,因为后面需要使用。 任务指针 volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • 学习ARM开发(20)
  • 学习ARM开发(19)
  • 学习ARM开发(14)
  • 学习ARM开发(15)
何立民专栏 单片机及嵌入式宝典

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

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