寄存器配置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))); } }

浙公网安备 33010602011771号