STM32 DMA使用详解
1、画图Cortex-M3内部结构图,要体现相应的总线,并标注其作用。
Icode:程序存在Flash中,通过ICode(Instruction Code)总线与Cortex连接取指令。
Dcode:数据被存放在外设内部Flash(SRAM)中,通过DCode(Data Code)访问。
Systembus: 访问外设的寄存器,通常说的寄存器编程就是用这条总线的。
DMA: 数据变量拷贝时可以不占用CPU,通过DMA(Direct Memory Access)总线和DMA1、DMA2完成。
2、关于RCC时钟,完成如下任务
(1)画出RCC时钟树简图,要体现出相关的时钟源
整个系统有四个部分需要时钟
USB
各种外设
RTC
看门狗
前两种主要是靠内部或外部高速时钟,默认复位后是内部高数时钟
RTC通过RTCsel来从HSE、LSE、LSI中选择一个
看门狗就是内部低速时钟
PLL准确的的来说并不是一个时钟,它是将时钟源传过来的时钟信号倍频再传给后续的外设
整个时钟树里有四个选择开关:
1.PLLXTPRE 倍频前分频选择开关,在传给PLLMUTL倍频前要不要分频
2.PLLSRC 倍频前输入信号来源选择开关,是选择内部的还是外部的
3.SW 各种外设时钟源选择开关,可以选择内部的还是外部的还是PLL倍频后 上面PLL倍频后的有内部倍频后的还是外部直接倍频 后的还是外部分频再倍频后的 所以SW切换后的有5种不同路径的时钟源个各种外设
4.RTCsel RTC时钟源选择开关,可以切换三种来源,一高速外部时钟128分频后,二外部低速,三内部低速
复位后默认的都是内部高速和内部低速时钟源,倍频也没有打开
(2)简述由8M晶振到72M主频的过程,以及通过寄存器方式配置72M主频的过程
要达到72MHz ,那就必须要开倍频,且必须使用外部高速时钟,内部高速时钟在倍频前有一个2分频所以最高只能倍频到64MHz
以外部晶振为8MHz来说,路径是,
PLLXTPRE不分频-> PLLSRC选外部高速 -> PLLMULT 设为9倍频 -> SW切换为PLL输入
上述路径可以使系统主频达到72MHz
以下是整个的配置过程
RCC_CR 地址:0x40021000 + 0 = 0x40021000 复位值:0x00000083 16位HSEON置一,然后判断17位HSERDY,外部时钟就绪后进性下一步
RCC_CFGR 地址:0x40021000 + 0x04 = 0x40021004 复位值:0x00000000 就是我们上边说的哪个顺序,16位PLLSRC置1,17位PLLXTPRE置0 ,然后18-21位的PLLMUL
置0111,4-7位AHB预分频置0000不分频,8-10位APB1预分频置100,2分频(因为ahb是72mhz,apb1最高可以36mhz,所以必须分频)。11-13位APB2预分频置000,不分频。
RCC_CR 24位PLLON置一,判断25位PLLRDY,为1说明就绪,继续下一步。
RCC_CFGR 0-1位SW置10,PLL输出作为系统时钟,然后判断2-3位,是否为10(PLL输出作为系统时钟)
STM32配置时钟为72mhz
STM32系统时钟设置详解
(3)阅读如下代码,理解并解释每一步的作用
typedef unsigned short int uint16_t; //定义一个short型变量占两个字节长度
typedef unsigned int uint32_t; //定义一个int型变零占四个字节长度
#define _IO volatile
#define PERIPH_BASE ((uint32_t)0x40000000) //定义外设的基地址
#define APB2PPERIPH_BASE (PERIPH_BASE + 0x10000)//定义外设总线APB2的基地址
#define GPIOC_BASE (APB2PPERIPH_BASE + 0x1000)//定义外设总线APB2上挂载的GPIOC基地址
#define GPIOC (GPIOC_BASE)//定义GPIOC引脚的基地址
#define GPIOC_CRH (GPIOC + 0x04) //定义GPIOC端口配置高寄存器
#define GPIOC_ODH (GPIOC + 0x0c)//定义端口数据输出寄存器
#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000)//定义高速总线AHB的基地址
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)//定义RCC时钟基地址
#define RCC (RCC_BASE) //定义RCC时钟基地址
#define RCC_APB2ENR (RCC + 0x18) //定义使能挂载在APB2上的外设的寄存器
#define RCC_CR (RCC + 0x00)//定义时钟控制寄存器
#define RCC_CFGR (RCC + 0x04)//定义时钟配置寄存器
#define FLASH_R_BASE (AHBPERIPH_BASE + 0x2000) //定义AHB上挂载的flash接口地址
#define FLASH (FLASH_R_BASE )
#define FLASH_ACR (FLASH + 0x00)
void RCC_init(uint16_t PLL) //传入倍频数PLL
{
uint32_t temp = 0;
*((uint32_t *)RCC_CR) |= 0x00010000;//将HSEON位置1
while (!(*((uint32_t*)RCC_CR) >> 17)); //等待HSERDY位硬件置1后跳出循环
*((uint32_t *)RCC_CFGR) = 0X00000400; //将PPRE1位置100,,将HCLOCK二分频为PCLOCK1
PLL -= 2; //PLLMUL位是从0000开始二倍频,所以要将传入的倍频数减去一个2等于PLLMUL位实际应该写入的真实值
*((uint32_t *)RCC_CFGR) |= PLL << 18; //将倍频位置为需要的倍频
*((uint32_t *)RCC_CFGR) |= 1 << 16; //将PLLSRC位置1,选择HSE为倍频输入时钟源
*((uint32_t *)FLASH_ACR) |= 0x2; //将LATENCY位置为010,两个等待状态,闪存读取要和时钟频率一致不然程序会跑飞
*((uint32_t *)RCC_CR) |= 0x01000000; //将PLLON位置1,开启PLL倍频
while (!(*((uint32_t*)RCC_CR) >> 25)); //等待PLLRDY位硬件置1后跳出循环
*((uint32_t *)RCC_CFGR) |= 0x00000002; //将SW位置1,选择倍频后的时钟源作为系统时钟源
while (temp !=0x02) //等待SWS位硬件置为10时,PLL成功作为系统时钟源输出
{
temp = *((uint32_t *)RCC_CFGR) >> 2; //将SWS的两位移到最右边
temp &= 0x03; //与11取与
}
}
3.flash寄存器设置延时周期的作用
STM32配置时钟时注意设置FLASH等待周期
4.通过寄存器点亮led
typedef unsigned int u32;
#define GPIOC_BASE0x40011000
#define PERIPH_BASE0x40000000
#define APB2_BASE(PERIPH_BASE+0x10000)
#define AHB_BASE(PERIPH_BASE+0x20000)
#define RCC_BASE(AHB_BASE+0x1000)
#define RCC_APB2ENR(RCC_BASE+0x18)
#define GPIOx_CRH_OFF0x0004
#define GPIOx_ODR_OFF0x000C
#define GPIOx_CRH (GPIOC_BASE+GPIOx_CRH_OFF)//0x4001 1004
#define GPIOx_ODR(GPIOC_BASE+GPIOx_ODR_OFF)//0x4001 100C
void delay(u32 x)
{
u32 i = 0;
while(x--)
{
i = 10000000;
while(i--);
}
}
int main(void)
{
*((unsigned int *)RCC_APB2ENR) |= 0x00000010;
//*((unsigned int *)GPIOx_CRH) = 0x00300000;//error
//先清除寄存器相应位的值
*((unsigned int *)GPIOx_CRH) &= 0xff0fffff;
//然后再进行对相应位赋值
*((unsigned int *)GPIOx_CRH) |= 0x00300000;
//*((unsigned int *)GPIOx_CRH) = *((unsigned int *)GPIOx_CRH) + 0x00300000;
//*(GPIOx_CRH) = 0x00300000;
*((unsigned int *)GPIOx_ODR) &= ~(1<<13);
*((unsigned int *)GPIOx_ODR) |= 0x00000000;
//闪烁
while(1)
{
//亮
*((unsigned int *)GPIOx_ODR) &= ~(1<<13);
*((unsigned int *)GPIOx_ODR) |= 0x00000000;
//延时一些时间
//sleep(2);
delay(1);
//循环
//灭
*((unsigned int *)GPIOx_ODR) &= ~(1<<13);
*((unsigned int *)GPIOx_ODR) |= 0x00002000;//0000 0000 0000 0000 0010 0000 0000 0000
//延时一些时间
delay(1);
}
return 0;
}
上面一种是直接对ODR寄存器输出高低电平来控制亮灭
下面这种是通过BSRR寄存器来控制输出高低电平来控制亮灭
/*******************************************************************************
--------------------------------------------------------------------------------
* 实 验 名 : 使用寄存器点亮一个LED
* 实验说明 : 操作寄存器控制LED1指示灯闪烁
* 连接方式 :
* 注 意 :
*******************************************************************************/
#include "stm32f10x.h"
typedef unsigned int u32; //类型重定义 unsigned int -- u32
void SystemInit()
{
}
/*******************************************************************************
* 函 数 名 : delay
* 函数功能 : 延时函数,通过while循环占用CPU,达到延时功能
* 输 入 : i
* 输 出 : 无
*******************************************************************************/
void delay(u32 i)
{
while(i--);
}
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
int main()
{
RCC_APB2ENR |= 1<<4;
GPIOC_CRH &= ~( 0x0F<< (4*5));//将MODE13和CONF13四位清零
GPIOC_CRH |= (1<<4*5); //将MODE13和CONF13四位置为0100
GPIOC_ODR &=~(1<<13);//配置输出低电平
GPIOC_BSRR=(1<<(16+13));
while(1)
{
GPIOC_BSRR=(1<<(16+13));
delay(0x1FFFFF);
GPIOC_BSRR=(1<<(13));
delay(0x1FFFFF);
}
}
————————————————
版权声明:本文为CSDN博主「GK小卜」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42224577/article/details/114745801