【连载】STM8L051F3_12_DMA应用

[复制链接]
1321|0
手机看帖
扫描二维码
随时随地手机跟帖
caijie001|  楼主 | 2018-5-24 18:02 | 显示全部楼层 |阅读模式
本文介绍STM8L051F3的DMA相关知识。内容分为以下几部分:
  • DMA简介
  • DMA传输ADC数据
DMA(Direct memory access):直接内存存储。DMA可以提高外设和内存以及内存到内存直接的传输速率,数据能不经过CPU任何的参与快速地移动,这样可以节省CPU去处理其他事情。以STM8L051F3为例,它的DMA控制器一共有4个人通道,每个通道专门用于从一个或多个外设内存访问请求,它也能仲裁DMA请求的优先级。DMA的主要特点如下:
  • 4个通道在多个外设之间共享
  • 数据传输可以从外设到内存、内存到外设、内存到内存
  • 硬件/软件可以根据每个通道的优先级进行仲裁
  • 可编程传输数据量:最多达255个数据块(字节或字)
  • 递增和递减寻址模式
  • 可硬件和软件编程通道优先级
  • 在半发送和发送结束可选择中断
  • 可软件编程发送数据大小:8位或16位
  • 通道请求直接的优先级:可软件编程(非常高、高、中、低)或在相同的情况下硬件可编程
  • 软件触发器也支持内存通道,取决于硬件配置
  • 两个标志位(DMA半传输,DMA传输完成)对每个通道的单个中断请求进行逻辑上的或
  • 循环缓冲区管理(自动装载模式)
  • 挂起和重新开始DMA发送能力
  • 运行在低功耗模式能力(WFI或WFE)
DMA模块的框图如下:


DMA传输。一个事件之后,外设发送一个请求信号到DMA控制器,DMA控制器根据通道的优先级来对请求进行处理,一旦DMA控制器访问外设,DMA控制器就会发送一个应答给外设,如果外设没有其他等待的请求,得到DMA控制器的应答后就会释放它的请求信号。
DMA通道。4个通道:3个普通通道(通道0、通道1、通道2)和一个内存通道(通道3)。普通通道处理位于固定地址的外设寄存器和一个自动递增/递减指针指向的内存地址范围之间的数据传输。内存通道也是一个普通通道,但是它可以处理两个内存指针指向的内存地址之间的数据传输。
可编程的数据大小。发送的数据大小(8位或16位)可以通过DMA_CXSPR寄存器的TSIZE位来控制(当运行在16位模式是,系统处理字节存储地址在递增或递减模式,目标和源指针必须包含MSB地址)。配置成16位模式,传输由4个连续的8位读写操作:
  • 从源数据的偶地址读取MSB
  • 从源数据的奇地址读取LSB
  • 往目标的偶地址写入MSB数据
  • 往目标的奇地址写入LSB数据
为了保证数据的连贯性,两个读操作和两个写操作是不可分割的。
指针增量。在内存到外设或外设到内存传输期间,每次传输后内存指针会自动地递增或递减,这取决于DMA_CXCR寄存器的MINCDEC位,而外设的地址是固定的。
通道配置步骤。可根据以下步骤配置DMA的通道x(x为通道号):
1)在DMA_CXPARL/H寄存器设置外设寄存器地址
2)在DMA_CXPARH/L和DMA_CXM0ARH/L寄存器设置内存地址
3)在DMA_CXNDTR寄存器配置发送数据的数量
4)在DMA_CXSPR寄存器的PL[1:0]位配置通道优先级
5)在DMA_CXCR寄存器配置数据传输方向、循环模式、内存递增/递减模式、发送数据大小、在半传输或传输完成中断等
6)在DMA_CXCR寄存器设置EN位激活通道
7)在所有通道配置完成后,配置DMA_GCSR寄存器GEN位使能DMA
其他的循环模式、内存到内存模式、DMA传输暂停、等相关的详细说明可以参考官方手册RM0031的第13章内容。DMA1(STM8L051F3的DMA外设是DMA1)通道请求映射如下:


2、DMA传输ADC数据2.1 DMA配置本小节介绍通过ADC采集电压数据,通过DMA把数据转送到内存,然后设置TIM3_CH1(接的是PB1,也就是LED1)为PWM1模式,再通过DMA把内存中ADC的数据传送到TIM3的CCR1寄存器上,实现根据ADC采集电压的变化来控制LED1的亮度。
实验的过程可以理解为:外设--DMA--内存--DMA--外设。实验使用三个外设功能:
  • ADC1--配置ADC1_CH22,连续转换模式,12位分辨率,使用DMA通道1(外设到内存)
  • TIM3--配置TIM3_CH1为PWM1模式,使用通道0(内存到外设)
  • DMA--配置通道0与通道1的模式、参数以及使能
使用的例程:STM8L051F3_12_DMA,ADC的配置步骤如下:
1)使能ADC1外设时钟
2)初始化ADC1通道22(PD0)IO口为浮空输入模式
3)初始化ADC1:连续转换模式,12位分辨率,ADC时钟2分频
4)配置ADC低速通道,采样周期384
5)使能ADC1
6)使能ADC1通道22
DMA的配置步骤如下(包括ADC-->内存与内存-->TIM3):
1)使能DMA1外设时钟
2)映射ADC1到DMA通道1
3)DMA初始化:通道1、内存地址BUFFER_ADDRESS、外设地址ADC1_DR_ADDRESS、数据大小BUFFER_SIZE、传输方向外设到内存、DMA模式循环、内存增长模式增长、DMA优先级高、内存数据大小半字
4)DMA初始化:通道0、内存地址BUFFER_ADDRESS、外设地址TIM3_CCR1_ADDRESS、数据大小BUFFER_SIZE、传输方向内存到外设、DMA模式循环、内存增长模式增长、DMA优先级高、内存数据大小半字
5)使能DMA1通道1
6)使能DMA1通道0
7)使能DMA1
TIM3配置(配置TIM3_CH1为PWM输出)步骤如下:
1)使TIM3外设时钟
2)TIM3基本配置:时钟分频1、向上计数、周期0xFFF
3)配置TIM3的通道1作为PWM输出
4)使能TIM3
5)配置TIM3_CH1(PB1)的GPIO
6)使能TIM3通道1的PWM输出
2.2 例程介绍所有的函数实现都在main.c文件中,ADC配置函数如下:
static void ADC_Config(void)
{
  /* 使能 ADC1 时钟 */
  CLK_PeripheralClockConfig(CLK_Peripheral_ADC1, ENABLE);
  //配置ADC1通道22的IO口为浮空输入模式
  GPIO_Init(GPIOD, GPIO_Pin_0, GPIO_Mode_In_FL_No_IT);
  /* 初始化 ADC1 连续转换,12位分辨率, ADC时钟分频2 */
  ADC_Init(ADC1, ADC_ConversionMode_Continuous, ADC_Resolution_12Bit, ADC_Prescaler_2);
  /* 配置ADC1 低速通道, 采样周期384 */
  ADC_SamplingTimeConfig(ADC1, ADC_Group_SlowChannels, ADC_SamplingTime_384Cycles);
  /* 使能 ADC1 */
  ADC_Cmd(ADC1, ENABLE);
  /* 使能ADC1通道22 */
  ADC_ChannelCmd(ADC1, ADC_Channel_22, ENABLE); /* connected to potentiometer */
}
TIM3的配置函数如下:
static void TIM3_Config(void)
{
  /* 使能 TIM3 时钟 */
  CLK_PeripheralClockConfig(CLK_Peripheral_TIM3, ENABLE);
  /* TIM3基本配置:时钟分频:1,向上计数,周期:0xfff */
  TIM3_TimeBaseInit(TIM3_Prescaler_1, TIM3_CounterMode_Up, 0xFFF);
  /* 配置TIM3_CH1作为PWM输出 */
  TIM3_OC1Init(TIM3_OCMode_PWM1, TIM3_OutputState_Enable,
               0x7FF, TIM3_OCPolarity_Low,
               TIM3_OCIdleState_Set);
  /* 使能 TIM3 */
  TIM3_Cmd(ENABLE);
  /* 配置TIM3_CH1的GPIO (PD2)*/
  GPIO_Init(GPIOB, GPIO_Pin_1 , GPIO_Mode_Out_PP_Low_Fast);
  /* 使能TIM3的PWM输出*/
  TIM3_CtrlPWMOutputs(ENABLE);
}
DMA配置函数如下:
static void DMA_Config(void)
{
  /* 使能 DMA1 时钟 */
  CLK_PeripheralClockConfig(CLK_Peripheral_DMA1, ENABLE);
  /* 连接ADC1到DMA1通道1 */
  SYSCFG_REMAPDMAChannelConfig(REMAP_DMA1Channel_ADC1ToChannel1);
  //*初始化DMA1通道1, 内存地址:BUFFER_ADDRESS,外设地址:ADC1_DR_ADDRESS,
//数据大小:BUFFER_SIZE,传输方向:外设到内存,DMA模式:循环,内存地址模式:增长,
//DMA优先级:高,内存数据大小:半字
  DMA_Init(DMA1_Channel1, BUFFER_ADDRESS, ADC1_DR_ADDRESS, BUFFER_SIZE,  DMA_DIR_PeripheralToMemory,
     DMA_Mode_Circular, DMA_MemoryIncMode_Inc, DMA_Priority_High,  DMA_MemoryDataSize_HalfWord);
  // 初始化DMA1通道0, 内存地址:BUFFER_ADDRESS,外设地址:ADC1_DR_ADDRESS,
//数据大小:BUFFER_SIZE,传输方向:内存到外设,DMA模式:循环,内存地址模式:增长,
//DMA优先级:高,内存数据大小:半字
  DMA_Init(DMA1_Channel0, BUFFER_ADDRESS, TIM3_CCR1_ADDRESS, BUFFER_SIZE,  DMA_DIR_MemoryToPeripheral,
DMA_Mode_Circular, DMA_MemoryIncMode_Inc, DMA_Priority_High,  DMA_MemoryDataSize_HalfWord);
  /* 使能DMA1通道1 */
  DMA_Cmd(DMA1_Channel1, ENABLE);
  /* 使能DMA1通道0 */
  DMA_Cmd(DMA1_Channel0, ENABLE);
  /* DMA1 使能 */
  DMA_GlobalCmd(ENABLE);
}
DMA配置相关的地址定义如下:
//定义内存与外设的地址,用于DMA传输
#define ADC1_DR_ADDRESS      ((uint16_t)(ADC1_BASE + 0x04))
#define TIM3_CCR1_ADDRESS    ((uint16_t)(TIM3_BASE + 0x11))
#define BUFFER_SIZE          ((uint8_t) 0x01)
#define BUFFER_ADDRESS       ((uint16_t)(&Buffer))
uint16_t Buffer = 0;    //定义变量保存DMA数据,作为DMA传输内存地址
主函数如下:
void main(void)
{
  ADC_Config(); //ADC配置
  DMA_Config(); //DMA配置
  TIM3_Config();//TIM3配置
  /* 使能 ADC1 DMA 请求*/
  ADC_DMACmd(ADC1, ENABLE);
  /* 使能 TIM1 DMA 请求*/
  TIM3_DMACmd(TIM3_DMASource_Update, ENABLE);
  /* 起始ADC1转换*/
  ADC_SoftwareStartConv(ADC1);
while(1)
{
;
}
}
使用ST-LINK把程序下载到开发板,ADC1_CH22(PD0)采样电压越高LED1越亮,检测的测试方法:把PD0口接板子的GND,LED1灭;接板子的VCC,LED1亮(如果把PD0接电位器,那么LED1的亮度可以由电位器调节)。

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:21ic公开课专区:http://open.21ic.com/ 21ic资料下载中心:http://dl.21ic.com/ 21ic项目外包中心:http://project.21ic.com/ 杰杰欢迎大家有空常来赛事专区逛逛

131

主题

3790

帖子

63

粉丝