具体题目和个人想法
(1)用直接配置串口的方式,向PC传输一句话,(2)在内存中存储一句话,并用DMA配置串口向PC传输。这是原题,笔者因为不可抗力(看错题了)就把输入输出一起做了,所以大伙凑合着看吧doge
在STM32上的串口是USB转串口,函数主要是USART1,含有两个引脚,分别是USART1_TX(输出)对应引脚PA9,USART1_RX(接收)对应引脚PA10,那么在我们使用DMA(直接内存访问)之前先编写usart.h和usart.c来实现PC端的发送和单片机端的接收。
编写usart(串口)头文件及其初始化
刚刚在前文提到了USART1这一串口,大家可以直接go to define找到stm32f10x_usart.h,那么这就是我们编写串口函数时所必须的头文件。
同样,我们这里介绍一下如何使用串口输出字符或者是字符串,其实和平常C语言编程思路大差不差,唯一的问题在于我们每一个变量都是指针指向所输入的字符的地址,字符串就构造数组存储,再指向数组即可。接下来看一看sendbyte和sendstring。
void Usart_SendByte( USART_TypeDef * pUSARTx,uint8_t ch)//一个指针和8位字符(字符8位)
{
USART_SendData(pUSARTx,ch);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE)==RESET);
}//获取该指针并将FLAG RESET
void Usart_SendString( USART_TypeDef * pUSARTx,char *str)//char型的数组指针
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str +k));//依次读取字符输出形成字符串
k++;
}
while(*(str+k)!='\0');//判断是否输出完毕
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)//重置
{}
}
接着就是和之前一样的,因为usart仍然由GPIO寄存器控制,所以和之前一样的GPIO初始化配置就行了...吗?同学们需要注意到这次和之前不同,前面的LED和beep我们的1/0都是通过GPIO直接控制的,所以不需要去考虑如何断开的问题,但是串口一旦插上去一定是都处于相互传输的状态,那我们怎么去让他停止呢?
这里我们就需要另一个非常重要的函数——中断函数了,它所作的事情就是到时间了就直接中断,我们来看看他的函数:
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStruct;//初始化
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2 );//优先级族为2
NVIC_InitStruct.NVIC_IRQChannel = USART_IRQ;//确定控制的中断通道是usart
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;//优先权均定为1
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//时钟使能
NVIC_Init(&NVIC_InitStruct);
}
那么我们来看看编写好的usart.c和usart.h吧(注意波特率统一,笔者这里给的是115200)
#include "stm32f10x_gpio.h"
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include "usart.h"
#include <stdio.h>
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig (NVIC_PriorityGroup_2 );
NVIC_InitStruct.NVIC_IRQChannel = USART_IRQ;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
USART_GPIO_APBxClkCmd(USART1_GPIO_CLK,ENABLE);
USART_APBxClkCmd(USART_CLK,ENABLE);
GPIO_InitStructure.GPIO_Pin = USART1_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(USART1_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART1_RX_GPIO_PIN;
GPIO_InitStructure .GPIO_Mode = GPIO_Mode_IN_FLOATING ;//浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(USART1_RX_GPIO_PORT, &GPIO_InitStructure);
USART_InitStructure .USART_BaudRate =USART_BAUDRATE;
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_Rx |USART_Mode_Tx ;
USART_Init (USART1,&USART_InitStructure );
USART_ClearFlag(USART1,USART_FLAG_TC);
NVIC_Configuration();
USART_ITConfig (USARTx,USART_IT_RXNE ,ENABLE );
USART_Cmd (USARTx,ENABLE);
}
void Usart_SendByte( USART_TypeDef * pUSARTx,uint8_t ch)
{
USART_SendData(pUSARTx,ch);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE)==RESET);
}
void Usart_SendString( USART_TypeDef * pUSARTx,char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str +k));
k++;
}
while(*(str+k)!='\0');
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
void USART_IRQHandler(void)//将receive的data传出
{
uint8_t ucTemp;
if(USART_GetITStatus (USART1 ,USART_IT_RXNE)!=RESET )
{
ucTemp = USART_ReceiveData (USART1);
USART_SendData (USART1,ucTemp );
//很熟悉的方式(临时参数)
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
}
USART_ClearFlag(USART1,USART_FLAG_TC);
}
#ifndef __USART_H
#define __USART_H
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
void USART_Config(void);
static void NVIC_Configuration(void);
void Usart_SendByte( USART_TypeDef * pUSARTx,uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx,char *str);
void USART_IRQHandler(void);
//记得一定要声明
#define USARTx USART1
#define USART_BAUDRATE 115200
#define USART1_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd
#define USART1_RX_GPIO_PORT GPIOA
#define USART1_RX_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART1_RX_GPIO_PIN GPIO_Pin_10
#define USART1_TX_GPIO_PORT GPIOA
#define USART1_TX_GPIO_CLK RCC_APB2Periph_GPIOA
#define USART1_TX_GPIO_PIN GPIO_Pin_9
#define USART_APBxClkCmd RCC_APB2PeriphClockCmd
#define USART_CLK RCC_APB2Periph_USART1
#define USART_IRQ USART1_IRQn
#define USART_IRQHandler USART1_IRQHandler
#define USART_TX_DMA_CHANNEL DMA1_Channel4//懒得删了大家凑合看
#define USART_DR_ADDRESS (USART1_BASE+0x04)//给出地址,可以在库函数中查
#define SENDBUFF_SIZE 5000//预留的内存量
#endif
主函数(1)
看看还没有用DMA,可以输入输出的主函数吧
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
#include "usart.h"
int main (viod)
{
USART_Config();
while(1)
{}
}
打开串口调试助手可以看到Rx和Tx
进行输入就会在上面产生字符,同时Rx和Tx的Bytes是同时增加,这样就完成了PC端输出单片机接收和单片机输出OC端接收。
编写DMA文件
DMA本质上是直接内存访问的寄存器,那么既然是寄存器,就和GPIO的差不了太多,但是地址时钟等等大家需要仔细辨别一下。老样子,先源文件后头文件。
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#include "dma.h"
void DMAx_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 par,u32 mar,u16 ndtr)
{
DMA_InitTypeDef DMA_InitStructure;
//定义初始化结构体
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//时钟使能
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//par为基地址的名称变量,便于修改
DMA_InitStructure.DMA_MemoryBaseAddr = mar;//mar为寄存地址名称变量,便于修改
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = ndtr;//输入的数据大小设为变量ndtr便于修改
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//dma模式为正常
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级为中
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMAy_Channelx,&DMA_InitStructure);
}
void DMAx_Enable(DMA_Channel_TypeDef* DMAy_Channelx,u16 ndtr)
{ //此函数为了中断后重充能重置,可以直接使能编进上一个函数中
DMA_Cmd(DMAy_Channelx,DISABLE);
DMA_SetCurrDataCounter(DMAy_Channelx,ndtr);
DMA_Cmd(DMAy_Channelx,ENABLE);
}
#ifndef __DMA_H
#define __DMA_H
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_usart.h"
#include "usart.h"
void DMAx_Init(DMA_Channel_TypeDef* DMAy_Channelx,u32 par,u32 mar,u16 ndtr);
void DMAx_Enable(DMA_Channel_TypeDef* DMAy_Channelx,u16 ndtr);
#endif
主函数(2)
那么话不多说,直接看主函数代码:
#include "stm32f10x.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_gpio.h"
#include "usart.h"
#include "dma.h"
#define send_buf_len 1
//后续的输入数据长度,可以随便设,决定你后续在输出过指定的string后输出多少字节
u8 send_buf[send_buf_len ];
int main (void)
{
DMAx_Init(DMA1_Channel4,(u32)&USART1->DR,(u32)send_buf,send_buf_len);
USART_Config();
Usart_SendString (USART1,"any words you want\n");//字符串随便写,除了中文,没ASSIC码
DMAx_Enable(DMA1_Channel4,send_buf_len);
USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
while(1)
{
if(DMA_GetFlagStatus(DMA1_FLAG_TC4)!=RESET)
{
DMA_ClearFlag (DMA1_FLAG_TC4);
break;
}
}
}
在串口调试助手看看实现的功能
完美输出!