硬件平台:STM32F10X内部DMA模块 + USART模块 + JLink
软件平台:Keil 4
一、基础知识
DMA 直接内存访问----Directional Memory Access,大二 《微机原理》里面讲8086的时候就已经讲过了,只是当时没有实际用过而已。
自己理解:DMA,其实就相当于一个公司的库管而已,当CPU--老板把数据的传送,即相当于货物的运输交给库管DMA的时候,数据的传送就不用老板自己实时查看管理了,由DMA模块自动完成,提高了CPU的使用效率。
DMA一种高速的数据传输操作,允许在外部设备和存储器之间直接读写数据,既不通过CPU,也不需要CPU干预。整个数据传输操作在一个称为"DMA控制器"的控制下进行的。CPU除了在数据传输开始和结束时做一点处理外,在传输过程中CPU可以进行其他的工作。这样,在大部分时间里,CPU和输入输出都处于并行操作。
DMA组成:
1、主存地址寄存器
2、数据数量计数器
3、DMA的控制/状态逻辑
4、DMA请求触发器
5、数据缓冲寄存器
6、中断机构
DMA的缺点:
因为DMA允许外设直接访问内存,从而形成对总线的独占。
这在实时性强的硬实时系统的嵌入式开发中将会造成中断延时过长
二、发送程序例程
程序涉及的模块有:
RCC:复位及时钟控制模块,用于初始化STM32 外设时钟及IO口复用时钟;
USART:通用同步异步收发器,即串口,用于发送数据至上位机显示已发送的数据;
GPIO:通用输入输出口复用配置模块;
DMA: DMA模块配置;
Delay:延时等待模块;
NVIC:中断配置模块。
RCC
//RCC时钟配置
void RCC_cfg(void)
{
ErrorStatus HSEStartUpStatus;
//定义错误状态变量
RCC_DeInit();将RcC初始化,重新设置为默认值
RCC_HSEConfig(RCC_HSE_ON);
//打开外部高速时钟晶振,使能HSE
/*RCC_HSE_ON 开
_off 关 _bypass hse晶振被外部时钟旁路*/
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/*RCC_WaitForHSEStartUp()返回一个ErrorStatus枚举值,
success好,error未好*/
if(HSEStartUpStatus == SUCCESS)//HES就绪
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//AHB时钟(HCLK)=系统时钟
RCC_PCLK1Config(RCC_HCLK_Div2);
//设置低速AHB时钟(APB1)为HCLK的2分频
RCC_PCLK2Config(RCC_HCLK_Div1);
//设置高速AHB时钟(APB2)=HCLK时钟
FLASH_SetLatency(FLASH_Latency_2);
//设置FLASH代码延时
//使能领取指缓存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//设置PLL时钟源及倍频系数,为HSE的9倍频 8MHz * 9 = 72MHz
/*void RCC_PLLConfig(u32 RCC_PLLSource, u32 RCC_PLLMul)
RCC_PLLSource_HSI_Div2 pll输入时钟=hsi/2;
RCC_PLLSource_HSE_Div1 pll输入时钟 =hse
RCC_PLLSource_HSE_Div2 pll输入时钟=hse/2
RCC_PLLMul_2 ------_16 pll输入时钟*2---16
pll输出时钟不得超过72MHZ*/
RCC_PLLCmd(ENABLE);
//ENABLE / DISABLE
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待就绪
/*FlagStatus RCC_GetFlagStatus(u8 RCC_FLAG) 检查指定RCC标志位
返回SET OR RESET
RCC_FLAG_HSIRDY HSI晶振就绪
RCC_FLAG_HSERDY
RCC_FLAG_PLLRDY
RCC_FLAG_LSERDY
RCC_FLAG_LSIRDY.......*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//设置PLL为系统时钟源
/*void RCC_SYSCLKConfig(u32 RCC_SYSCLKSource) 设置系统时钟
RCC_SYSCLKSource_HSI
RCC_SYSCLKSource_HSE
RCC_SYSCLKSource_PLLCLK 选HSI HSE PLL 作为系统时钟*/
while(RCC_GetSYSCLKSource() != 0x08);
//判断PLL是否是系统时钟
/*u8 RCC_GetSYSCLKSource(void) 返回用作系统时钟的时钟源
0x00:HSI 0x04:HSE 0x08:PLL */
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_AFIO , ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//打开串口对应的外设时钟
/*EN/DIS AHB外设时钟 启动DMA1 时钟
RCC_AHBPeriph_DMA1
RCC_AHBPeriph_DMA2
RCC_AHBPeriph_SRAM
RCC_AHBPeriph_FLITF
RCC_AHBPeriph_CRC
RCC_AHBPeriph_FSMC
RCC_AHBPeriph_SDIO 等时钟 其中 SRAM 和FLITF 时钟只可在睡眠 SLEEP 模式下被失能*/
//U2 U3 时钟在APB1
//打开GPIO时钟,复用功能,串口1的时钟
/*void RCC_APB2PeriphClockCmd(u32 RCC_APB2Periph, FunctionalState NewState)
enable 或 disable apb2 外设时钟
RCC_APB2Periph_AFIO 功能复用IO 时钟
RCC_APB2Periph_GPIOA/B/C/D/E GPIOA/B/C/D/E 时钟
RCC_APB2Periph_ADC1/ADC2 ADC1/2 时钟
RCC_APB2Periph_TIM1
RCC_APB2Periph_SPI1
RCC_APB2Periph_USART1
RCC_APB2Periph_ALL 全部APB2外设时钟*/
}
GPIO
//IO口配置
void GPIO_cfg(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO_InitStructure初始化结构体为GPIO_InitTypeDef结构
GPIO_StructInit(&GPIO_InitStructure);
//B10 U3_TX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
//1、选中引脚GPIO_PIN_0--15 OR GPIO_PIN_ALL 选中全部管脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//2、GPIO_SPEED:GPIO_SPEED_10MHz/_2MHz/_50MHz 最高输出速率
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
/*Mode,工作状态:GPIO_MODE_AIN ----- 模拟输入
_IN_FLOATING ----- 浮空输入
_IPD ----- 上拉输出
_IPU ----- 上拉输入
_OUT_OD ----- 开漏输出
_OUT_PP ----- 推挽输出
_AF_OD ----- 复用开漏输出
_AF_PP ----- 复用推挽输出*/
GPIO_Init(GPIOB , &GPIO_InitStructure);
//B11 U3_RX
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//IO浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
//提示标示:LED显示串口正在发送数据/接收数据
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
USART
//串口初始化
void USART3_cfg(void)
{
USART_InitTypeDef USART_InitStructure;
USART_StructInit(&USART_InitStructure);
//将结构体设置为缺省状态
/*USART_StructInit通用同步异步串行口初始结构成员:
USART_BaudRate----9600,默认9600
IntegerDivider = ((APBClock) / (16 * (USART_InitStruct->USART_BaudRate)))
FractionalDivider = ((IntegerDivider - ((u32) IntegerDivider)) * 16) + 0.5
_WordLength 帧中传输的数据位----USART_WordLength_8b/_9b,字长宽8位
_StopBits 停止位 可为 USART_StopBits_1 ---帧结尾传输1个停止位
USART_StopBits_0.5 0.5个
USART_StopBits_2
USART_StopBits_1.5
_parity 奇偶模式 USART_Parity_No//奇偶失能,无奇偶
USART_Parity_even oumoshi
USART_Parity_odd jimoshi
奇偶使能时,在数据的MSB位插入奇偶位,字长9位时的第九位,8位时的第八位
_HardwareFlowControl // _none,硬件流控制失能
_rts 发送请求rts使能
_cts 清除发送cts使能
_rts_cts rts and cts 使能*/
//波特率设置为115200
USART_InitStructure.USART_BaudRate = 115200;
//一帧数据的宽度设置为8bits
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
//在帧尾传输1个停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
//奇偶检验失能模式,无奇偶校验位
USART_InitStructure.USART_Parity = USART_Parity_No;
//发送/接收使能
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
//硬件流控制失能
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
//设置串口1
USART_Init(USART3, &USART_InitStructure);
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
/*void USART_ITConfig(USART_TypeDef* USARTx, u16 USART_IT, FunctionalState NewState)
失能或使能相应的USART中断,x---1/2/3
_IT : _IT_PE 奇偶错误中断
_IT_TXE FASONG中断
_IT_TC 传输完成中断
_IT_RXNE 接收中断
_IT_IDLE 空闲总线中断
_IT_LBD LIN中断检测中断
_IT_CTS CTS中断
_IT_ERR 错误中断*/
USART_Cmd(USART3, ENABLE);
/*void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
失能或使能外设 x--1/2/3
NewState: USARTx ENABLE /DISABLE*/
}
NVIC
//配置中断
void NVIC_cfg(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
//定义NVIC初始化结构体 NVIC_InitStructure
//DMA 优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
//优先级 先占3 位 从占 1 位
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
//DAM1 通道4 中断 3.5的库 USART3_IRQn
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
//抢占式,先占 NVIC_IRQChannel 的先占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//响应式,从 NVIC_IRQChannel
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//使能
NVIC_Init(&NVIC_InitStructure);
//usart3 优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
//设置优先级分组 ,先占0 从占 4
NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
DMA
void DMA_cfg(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel2);
//将DMA的通道 X 寄存器重设为 缺省值
//DMA Channelx :x 1--7
//DMA1 通道4 配置
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART3->DR);
//定义DMA外设的基地址 U3 的数据寄存器
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Uart_Send_Buffer;
//内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
//DMA 传输方向 外设作为数据的目的地
DMA_InitStructure.DMA_BufferSize = 100;
//DMA 传输时 缓冲区的长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//外设地址寄存器增与否 不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//内存地址寄存器增与否 递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
//外设数据宽度 _byte 8bit _halfword 16bit _word 32bit
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;
//内存数据宽度
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//mode 设置了CAN 的工作模式 _Circular 循环缓存模式 _Normal 正常缓存模式
//当制定的DMA通道数据传输配置为内存到内存时 不能使用 循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
/*DMA通道 X 的优先级
_VeryHigh 有非常高的优先级
_High 高 _Medium 中 _Low 低*/
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
//DMA_M2M 使能DMA 通道 内存到内存传输 enable 设置为从内到内 disable 没有设置为从内到内
DMA_Init(DMA1_Channel2,&DMA_InitStructure);
//根据DMA_InitStructure指定的参数初始化 DMA 通道 X 的寄存器
DMA_ITConfig(DMA1_Channel2,DMA_IT_TC,ENABLE);
/*DMA_Channelx x: 1--7 ,DMA 通道
u32 DMA_IT 使能或失能 DMA 中断源 “ | ”同时选中多个DMA中断源
DMA_IT_TC 传输完成中断屏蔽
DMA_IT_HT 传输过半中断屏蔽
DMA_IT_TE 传输错误中断屏蔽
NewState : ENABLE or DISABLE*/
//DMA_Cmd(DMA1_Channel2, ENABLE);
//在主函数中再使能
//使能或失能指定的通道
USART_DMACmd(USART3,USART_DMAReq_Tx,ENABLE);
//enable or disable 相应USART的DMA———TX / RX 请求
USART_Cmd(USART3,ENABLE);
//开启串口3
}
欢迎讨论,共同学习