寄存器配置RCC

这里我翻车过,一度认为是晶振坏了,可是仔细想想不太对,最终才改过来,现在我就来说说自己是如何犯错并且发现问题的。

配置流程

1.看图

 

图片模糊一点没事,反正你都得去看中文参考手册高清无码大图。具体看图方法文字倒是不好描述,建议到去搜索视频讲解一看,然后脑子晕晕的再来看文字描述就会懂了。

2.看看参考手册上对外设的描述

你可能在看各种教程时就已经或多或少的知道了外设的描述(多少取决于视频老师讲的和理解的),但是,还是建议看一下参考手册里的描述。

3.认识寄存器

见中文参考手册

 

 

 就这些了,看看理解能力不太差的都能懂,啥位置1代表啥的,都写得超级详细的了

4.写写代码测试

不试一下永远不知道代码是错的。

见下面我亲手撸的代码:

void System_clock(void)
{
    volatile uint32_t  StartUpCount = 0,HSEStatus = 0;//时间向上计数    HSE状态标志
    
    RCC->CR |=(0x01<<16);//使能HSE
    
    //等待HSE稳定
    do{
        HSEStatus=RCC->CR & (0x01<<17);
        StartUpCount++;
    }while((HSEStatus==0) && (StartUpCount!=HSE_STARTUP_TIMEOUT));//HSE_STARTUP_TIMEOUT=0x0500
    if(StartUpCount!=HSE_STARTUP_TIMEOUT)
    {
        HSEStatus=0x01;
    }
    else
    {
        HSEStatus=0x00;
    }
    
    //如果启动成功
    if(HSEStatus==0x01)
    {
        //使能FLASH预存取缓存区(处理代码等待时间的设置)
        FLASH->ACR |= (0x01<<4);
        //配置时延位    配置高位低位可运行,配置低位高位会进入硬件错误,程序卡死
        FLASH->ACR &=0x00;
        FLASH->ACR |=(0x02<<0);
        
        RCC->CFGR &=0;//位清零
        RCC->CFGR |=(0x01<<16);//选择PLL时钟源
        RCC->CFGR &=~(1<<17);//HSE不分频
        RCC->CFGR |=(0x07<<18);//PLL锁相环倍频9
        RCC->CR |=(0x01<<24);//使能PLL
        while((RCC->CR & (0x01<<25))==0);//等待PLL稳定(锁定)
        RCC->CFGR &=~(0x08<<4);//AHB不分频
        RCC->CFGR |=(0x04<<8);//APB1分频2
        RCC->CFGR &=(0x07<<11);//APB2不分频
        RCC->CFGR |=(0x02<<14);//ADC分频6,只有12MHZ,达不到最大14MHZ
        RCC->CFGR |=(0x07<<24);//MCO时钟源PLL分频2,36MHZ
        RCC->CFGR |=(0x02<<0);//选择SYSCLK时钟源
        
        while(((RCC->CFGR & (0x08))==0)&&((RCC->CFGR & (0x0c)))==0x0c);//确保PLL作为系统时钟源
    }
    else
    {
        //启动失败,添加补救代码
    }
}

 

好了,到了这里,如何检测代码对错呢?通常有以下方法:1.使用MCO时钟输出,复用PA8引脚。优点就是100%正确检测;缺点就是你得有买示波器的钱,或者愿意跑十多分钟去实验室,用实验室示波器检测。2.使用滴答定时器加上LED灯直接观察,用手机粗略测量闪烁时间的间隔,与理论计算出来的值相比较。延迟时间越久,精确度越高。诶,这不就省钱省时间了吗?于是,我的噩梦开始了……

5.配置systick

这里的参考文献在Cortex_M3编程手册上,建议阅读英文原版,别说英语差之类的话,我英语也是不好的,抱着金山词霸字典啃呗(最近已经在翻译中断部分的内容,连同systick我也会一起翻译了,到时候翻译完了放出来就是。说实话我不喜欢现在已有的Cortex_M3中文编程手册,译者很多都按照自己的想法修改文档,排版,导致原本清晰的寄存器配置说明反而逐渐模糊,心里话,不喜勿喷。按照原文一比一翻译的,居然没有,逼得我只能自己翻译自己需要的部分)

 

 

由于还没有翻译完成,你们自己百度以上寄存器的配置吧,挺简单易懂的(这里按照顺序不采用开启systic中断的方法),于是我写了如下代码

/*滴答定时器延迟*/
/*注意nms不能超过1800*/
void delay_ms(uint16_t nms)
{
    uint32_t temp;
    SysTick->LOAD = 9000*nms;
    SysTick->VAL=0X00;//清空计数器
    SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
    do
    {
    temp=SysTick->CTRL;//读取当前倒计数值
    }while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
    SysTick->CTRL=0x00; //关闭计数器
    SysTick->VAL =0X00; //清空计数器
}

 

好了,随便开一个GPIO作为输出引脚控制亮灯即可

教学完成!!!!

 

不过此时,噩梦来临……

int main(void)
{    
    /*函数初始化区域*/
    System_clock();
    LED_Init(GPIOB,9);

    /*WHILE循环区域*/
    while(1)
    {
        GREEN_LED_ON;
        delay_ms(1000);
        GREEN_LED_OFF;
        for(i=0;i<10;i++)
        delay_ms(1000);
    }
}

 

我首先测试了以上代码,完美!!!果然是1s闪一下

够吗?不够,由于人反应时间会有误差,理论来说时间设置的越大反而精确度越高,我就来了一个delay_ms(9000);然后就出事了……

出错——上

我开始烧录代码,理论来说,应该亮灭各9s,但是实际情况却是亮灯和灭灯均在2s左右,我心态炸了,第一反应就是时钟配置失败,于是开始检查配置步骤,重复几次之后,发现没有错。我想是否是写入位操作时写偏了,于是又检查好几遍,还是没错。此时我还是较为冷静的,并且认为自己对于时钟理解没有错,配置不会有问题。心想既然软件上没错,会不会就是硬件出了错呢?但是很快排除,因为要是HSE出错,系统时钟默认配置为8MHz,算出来也不会为81s,更加慢才对。然后这才排除考虑硬件错误。最后会出错的,就是滴答定时器了。这是我第一次配置滴答定时器,看着说明很简单啊,我又对比了几遍说明,发现没有问题,然后抓狂了好久。

 

出错——中

我仔细上网查看库函数的配置,终于发现了一丝不同。他们使用for循环嵌套delay组成延迟函数,为啥呢?我觉得这里有猫腻,再三研究编程手册发现这一句话

 

 对喔,这是一个24位的寄存器,我很有可能是值设置大了,溢出了。经计算,像我这样配置,最大值只能为1863,不然就会超出范围。而9000远大于1863,问题解决。

 

出错——下

验证:

int main(void)
{    
    /*函数初始化区域*/
    System_clock();
    LED_Init(GPIOB,9);

    /*WHILE循环区域*/
    while(1)
    {
        GREEN_LED_ON;
        for(i=0;i<10;i++)
        {
            delay_ms(1000);
        }
        GREEN_LED_OFF;
        for(i=0;i<10;i++)
        {
            delay_ms(1000);
        }
        delay_ms(1000);
    }
}

 


理论来说会延迟10s,多次测量,正确,完美结束!!!!

 

学习心得:
一、RCC配置流程(HSE作为时钟源):
1.使能HSE,并等待HSE稳定(可做超时处理)
2.若没有超时,HSE成功启动:
a.配置FLASH预取缓冲区
b.配置HSE不分频
c.选择PLL时钟源
d.锁相环配置9倍频
e.使能PLL,并做等待处理(寄存器为RCC_CR)
f.选择SYSCLK时钟源
g.AHB不分频
h.APB1 2分频
i.APB2不分频
j.ADC 6分频,最终12MHz,达不到最大14MHz
k.MCO时钟源选择PLL/2,36MHz,最高可达到50MHz

二、学习滴答定时器简单计时(用以检测时钟配置是否正确)
1.计算计时一次需要时间(由于AHB不分频,Cortex内核系统时钟为9MHz,1/9us装载值加一)
2.配置LOAD,确定重装载数值
3.配置VAL,清空计数器
4.使能,并采用外部时钟源
5.读取当前倒计数值,等待时间到达
6.关闭计数器
7.清空计数器

三、注意注意ms不能超过1863,原因是Systick定时器就是系统滴答定时器,一个24位的倒计数定时器(16777215),计到0时,将从RELOAD寄 存器中自动重装载定时初值,在系统时钟为9MHz时,算出来的允许的ms计数值就不能大于1863,否则会发生数据溢出而计数失败可以采用 for循环解决问题(困扰了好久,还以为时钟配置错了,结果没有,哭了~~~~~~)

 

补充: /*2021-04-21    20:10:38*/

去了一趟实验室,把MCO部分做了,经历了示波器2通道损坏这种糟心事,还好我及时使用1通道测试了一下,分别测试了HSE,HSI,PLL/2的频率,实验检测数据与预期相符。

开启PA8引脚口代码,配置为复用推挽输出,50MHz

static uint8_t GPIO_RCC(GPIO_TypeDef * GPIOx)
{
    if(GPIOx==GPIOA)
    {
        RCC->APB2ENR |=((1)<<2);
    }
    else if(GPIOx==GPIOB)
    {
        RCC->APB2ENR |=((1)<<3);
    }
    else if(GPIOx==GPIOC)
    {
        RCC->APB2ENR |=((1)<<4);
    }
    else 
    {
        return 1;
    }
    return 0;
}

//MCO初始化
void MCO_Init(GPIO_TypeDef * GPIOx,uint16_t PINx)
{
    //开启GPIOx的时钟
    while(GPIO_RCC(GPIOx));
    if(PINx<=7)
    {
        GPIOx->CRL &=~(0x0f<<(4*PINx));
        GPIOx->CRL |=(0x0b<<(4*PINx));
    }
    else if(PINx>=8&&PINx<=15)
    {
        //CRH寄存器端位清空
        GPIOx->CRH &=~(0x0f<<(4*(PINx-8)));
        //CRH寄存器至于1011,复用推挽输出50MHz
        GPIOx->CRH |= (0x0b<<(4*(PINx-8)));
    }
}

 

posted @ 2021-04-21 17:23  LiXintao  阅读(618)  评论(0)    收藏  举报