10.2.4 DMA控制
接收缓冲区和发送缓冲区的DMA请求是独立的,它们分别对应于独立的DMA通道。
1.使用DMA进行发送
将USART控制寄存器3(USART_CR3)中的DMAT位置1可以使能DMA模式进行发送。
一旦使能了USART的DMA功能,当TXE 位置1时,
控制器会将数据自动从SRAM区加载到USART_DR,启动发送过程。
所有数据的发送不需要通过程序干涉。
2.使用DMA进行接收
将USART_CR3中的DMAR位置1可以使能DMA模式进行接收。一旦使能了USART的DMA功能,当接收数据时,RXNE
位置1时,控制器会将数据会从USART_DR自动加载到SRAM区域中。整个数据的接受过程不需要程序的干涉。
10.3 USART典型应用步骤及常用库函数
10.3.1 串口配置一般步骤
10.3.2 常用库函数说明
10.4 应用实例
10.4.1 通过串口向计算机传输100个字节
使用串口线把计算机和电路板的USART1连接在一起,如图11-15所示。编写程序,通过USART1向计算机发送100个字节,这一功能可以通过查询方式、中断方式或中断方式实现。这里以常用的查询方式实现这一功能。
USART1配置参数:
波特率=115 200、
有效数据位=8位、
停止位=1位、
不使用校验方式、
收发模式、
不使用硬件流控。
USART1的TXD和RXD分别使用PA9和PA10。
1.编程要点
(1)使能USART1和复用引脚GPIO的工作时钟。
(2)初始化USART1 相关GPIO引脚,并将引脚复用给USART1。
(3)根据要求,初始化USART1。使用查询方式发送数据。
(4)使能USART1。
(5)循环发送100个字节。
2.主程序
int main(void){ uint16_t i; /*初始化USART 配置模式为 115200 8-N-1,中断接收*/ USART1_Config(); for(i=0;i<100;i++)//循环发送100个x { /*发送一个字符x*/ USART_SendData(USART1, ‘x’); /*等待发送数据寄存器为空 ,只有在发送数据寄存器为空的情况下,才能发送下一个字符*/ while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } while(1); }
3. 串口配置函数
void USART_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; /*-------------------第1步--------------------*/ /*使能 GPIOA时钟*/ RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA,ENABLE); /*使能 UART1 时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); /*-------------------第2步--------------------*/ /*复用PA9到USART1*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); /*复用PA10到USART1*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); /*配置TX引脚为复用功能*/ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用模式 GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); /*配置RX引脚为复用功能*/ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF ; //复用模式 GPIO_Init(GPIOA,&GPIO_InitStructure); /*-------------------第3步--------------------*/ /*配置USART1模式*/ 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); //初始化USART1 /*因为使用查询方式发送数据,没有用到中断,因此没有中断相关配置,缺少第4步*/ /*-------------------第5步--------------------*/ USART_Cmd(USART1, ENABLE);//使能USART1}
10.4.2 串口与计算机回显功能实现
回显功能就是把计算机发送给电路板上微控制器的数据通过串口原样返回给计算机,并显示在串口显示软件中。
USART1配置参数:
波特率=115 200、
有效数据位=8位、
停止位=1位、
不使用校验方式、
收发模式、
不使用硬件流控。
USART1的TXD和RXD分别使用PA9和PA10。
1.编程要点
(1)使能USART1和复用引脚GPIO的时钟。
(2)初始化USART1 相关GPIO引脚,并将引脚复用给USART1。
(3)根据要求初始化USART1。
(4)初始化NVIC的USART1串口中断通道,并使能USART1的读取数据寄存器不为空中断(RXNE)。
(5)使能USART1。
(6)编写中断服务程序。
2.主程序
int main(void){ USART_Config(); //初始化USART1 printf("这是一个串口中断接收回显实验\n");//注意只有对printf函数进行重定向才能使用 while(1);}
3. 串口配置函数
void USART_Config(void){ GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; /*-------------------第1步--------------------*/ /*使能 GPIOA时钟*/ RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA,ENABLE); /*使能 UART1 时钟*/ RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); /*-------------------第2步--------------------*/ /*复用PA9到USART1*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1); /*复用PA10到USART1*/ GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); /*配置TX引脚为复用功能*/ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用模式 GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); /*配置RX引脚为复用功能*/ GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF ; //复用模式 GPIO_Init(GPIOA,&GPIO_InitStructure); /*-------------------第3步--------------------*/ /*配置USART1模式*/ 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); //初始化USART1 /*-------------------第4步--------------------*/ //初始化NVIC中的USART1中断通道 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //选择NVIC组2 NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn; //配置USART为中断源 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; //响应优先级为1 NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断 NVIC_Init(&NVIC_InitStructure); //初始化配置NVIC //使能串口的RXNE中断 USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //使能串口的RXNE中断 /*-------------------第5步--------------------*/ USART_Cmd(USART1,ENABLE); //使能USART1}
4.串口中断服务函数
void USART1_IRQHandler(void){ uint8_t ucTemp; if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) //检测RXNE标志位 { ucTemp = USART_ReceiveData( USART1 ); //读取接收数据 USART_SendData(USART1,ucTemp); //把数据发送给计算机,实现回显功能 } }
5. printf函数重定向
使用printf函数通过USART1向计算机的串口调试助手打印数据,需要将printf函数内部实现功能重定向到微控制器的USART1。
(1)设置Keil MDK软件中的Use MicroLIB选项。
(2)重定向fputc函数。
(2)重定向fputc函数。
int fputc(int ch, FILE* stream){//通过串级发送数据chUSART_SendData(USART1, (uint8_t) ch); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完毕 return ch;}
10.4.3 利用DMA通过串口向计算机1000个字节
利用DMA通过USART1向计算机传输1000个字节,一旦配置好USART1和DMA数据流,在启动DMA传输之后,不需要程序的干预,即可完成数据传输
USART1配置参数:波特率=115 200、有效数据位=8位、停止位=1位、不使用校验方式、收发模式、不使用硬件流控。
1.编程要点
(1)使能USART1、复用引脚GPIO及DMA2的时钟。
(2)初始化USART1相关GPIO引脚,并将引脚复用给USART1。
(3)根据要求,初始化并使能USART1。
(4)初始化DMA2数据流7的通道4,并使能DMA2数据流7。
(5)使能USART1的DMA发送功能,启动DMA数据传输。
2、主程序
uint8_t Send_Buff[1000]; //DMA传输数据源存储区int main(void){ uint16_t i; USART_Config(); //初始化USART1 DMA_Config(); //初始化DMA for(i=0;i<SENDBUFF_SIZE;i++) //初始化DMA传输数据源存储区 { Send_Buff [i] = 'Z'; } /*使能USART1的DMA发送,启动DMA数据传输*/ USART_DMACmd(USART1, USART_DMAReq_Tx,ENABLE); //关键 while(1); //无须CPU干涉,实现数据传输}
3. 串口配置函数
同10.4.1串口配置函数。
4. DMA配置函数
extern uint8_t Send_Buff[1000]; //DMA传输数据源存储区void DMA_Config(void){ DMA_InitTypeDef DMA_InitStructure; /*-------------------第1步--------------------*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE); //使能DMA2时钟 /*-------------------第2步--------------------*/ DMA_DeInit(DMA2_Stream7); //复位,禁止DMA2数据流7 while (DMA_GetCmdStatus(DMA2_Stream7)!= DISABLE); //确保DMA数据流禁止成功 /*-------------------第3步--------------------*/ /*初始化DMA2数据流7*/ DMA_InitStructure.DMA_Channel=DMA_Channel_7; //选择通道 DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t) SRC_Buffer;//目标地址 DMA_InitStructure.DMA_Memory0BaseAddr=(uint32_t) Send_Buff; //源数据地址 DMA_InitStructure.DMA_DIR=DMA_DIR_MemoryToPeripheral; //存储器到外设模式 DMA_InitStructure.DMA_BufferSize=1000; //DMA传输数据数目 //禁止自动递增功能 DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //使能自动递增功能 //8位数据宽度 DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize=DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //正常模式,不循环,只传输一次 DMA_InitStructure.DMA_Priority=DMA_Priority_High;//设置DMA2数据流7优先级为高DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable; //禁用FIFO模式 //此时这一参数无用DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single; //单次模式DMA_InitStructure.DMA_PeripheralBurst=DMA_MemoryBurst_Single; //单次模式DMA_Init(DMA2_Stream7,&DMA_InitStructure); //完成DMA2数据流7配置 /*-------------------第4步--------------------*/ DMA_Cmd(DMA2_Stream7,ENABLE); //使能DMA2数据流7,启动DMA数据传输 /*-------------------第5步--------------------*/ while (DMA_GetCmdStatus(DMA2_Stream7)!=ENABLE); //检测DMA数据流是否有效}