AMR开发学习底层库函数寄存器
STM32F103最小系统实现智能控制(寄存器实现)
一、硬件准备
1、学习前准备:整个项目使用C语言开发,所以,需要有良好的C语言基础,这里我就简单说明一下,C语言中,通用链表,函数指针,文件分类,这三点做好,在开发过程中会轻松很多。
2、硬件准备:网上购置stm32f103ze学习版,带有电阻屏的(电容屏贵,没钱),还有代码烧写器(jlink),还有语音解码模块,SD卡,DHT11,MP3播放模块,硬件连接图如下
3、软件准备:keil 4
二、实现功能
1、实现功能:时钟,贪吃蛇,音乐播放器,温湿度动态显示,记事本,LED灯控制(上述只能控制均可用语音控制实现)
关键代码剖析
1、时钟:芯片的最小单位都是寄存器,如何实现定时器,就如何实现时钟,对于时间的显示,只需要计算一个定时器,然后显示数字就好,这里使RTC时钟
void TimeCount_Install(u8 Time_nu,int Count_time,Tm_pHandle PHandle) { switch(Time_nu) { case TIM2_IRQn: case TIM3_IRQn: case TIM4_IRQn: RCC->APB1ENR |= (1<<(Time_nu-28)); RCC->CFGR &= ~(7<<8); //清空 RCC->CFGR |= (4<<8); //2分频 switch(Time_nu) { case TIM2_IRQn: TIM2->ARR = (Count_time-1); //确定重装载寄存器的值 72MHz=7200*10000,1s计数器产生中断 TIM2->PSC = (7200-1); //配置预分频器(TIMx_PSC) TIM2->DIER |= (1<<0); //允许跟新中断 break; case TIM3_IRQn: TIM3->ARR = (Count_time-1); //确定重装载寄存器的值 72MHz=7200*10000,1s产生中断 TIM3->PSC = (7200-1); //配置预分频器(TIMx_PSC) TIM3->DIER |= (1<<0); //允许跟新中断 break; case TIM4_IRQn: TIM4->ARR = (Count_time-1); TIM4->PSC = (7200-1); TIM4->DIER |= (1<<0); break; } break; case TIM5_IRQn: RCC->APB1ENR |= (1<<3); RCC->CFGR &= ~(7<<8); RCC->CFGR |= (4<<8); TIM5->ARR = (Count_time-1); TIM5->PSC = (7200-1); TIM5->DIER |= (1<<0); break; } IRQ_Install(Time_nu,PHandle); TimeCount_ON(Time_nu); //计数器打开,硬定时器 }
需要说明的是,硬定时器和软定时器,通俗来说,我们在做开发的时候,需要用到很多定时器,但是,对于M3系列的芯片来说,条件有限,所以,我们称,原本的定时器为硬定时器,所谓软定时器,就是,在硬定时器中,再次计数 ,假设硬定时器是1秒,则在里面技术时,那么我们就可以得到10秒钟的软定时器,但这里,为了精确,一般硬定时器都是毫秒,微秒级。(个人理解)
2、中断封装:
总共有60个外部中断,每一个中断都有一个中断动作函数,所以,我们在使用中断的时候,就可以在对应的中断函数里面自定义(类似于回调)但是,这样显得有些麻烦,我们可以在启动代码里面,将所有的中断动作函数定义为一个,如下:
我们是如何知道是哪一个中断产生了呢,我们可以通过NVIC寄存器,遍历中断编号,找出对应的中断信号,具体的代码如下:
void IRQ_Desacth() { int i,IRQ_NU; //循环遍历60个中断,找到动作中断编号 for(i=0;i<60;i++) { if(i<I2C1_ER_IRQn) { if((NVIC->IABR[0]>>i)& 1) { IRQ_NU=i; break; } } else if(i>=32) { if((NVIC->IABR[1]>>(i-I2C1_ER_IRQn))& 1) { IRQ_NU=i; break; } } } switch(IRQ_NU) { case EXTI0_IRQn: case EXTI2_IRQn: case EXTI3_IRQn: case EXTI4_IRQn: //?ж???? IRQ_Events[IRQ_NU].irqpHandle(); //中断结束之后,需要重新置1 EXTI->PR |= (1<<(IRQ_NU-6)); //重新置1 NVIC->ICPR[0] |= (1<<IRQ_NU); //中断解挂 break; case TIM2_IRQn: IRQ_Events[IRQ_NU].irqpHandle(); TIM2->SR &= ~(1<<0); //中断结束后,跟新中断标记 NVIC->ICPR[0] |= (1<<IRQ_NU); //中断解挂 break; case TIM3_IRQn: IRQ_Events[IRQ_NU].irqpHandle(); TIM3->SR &= ~(1<<0); NVIC->ICPR[0] |= (1<<IRQ_NU); break; case TIM4_IRQn: IRQ_Events[IRQ_NU].irqpHandle(); TIM4->SR &= ~(1<<0); NVIC->ICPR[0] |= (1<<IRQ_NU); break; case TIM5_IRQn: IRQ_Events[IRQ_NU].irqpHandle(); TIM5->SR &= ~(1<<0); NVIC->ICPR[1] |= (1<<(IRQ_NU-32)); break; case USART1_IRQn: // USART1->SR &= ~(1<<9); IRQ_Events[IRQ_NU].irqpHandle(); NVIC->ICPR[1] |= (1<<(IRQ_NU-32)); break; case RTC_IRQn: RTC->CRL &= ~(1<<0); IRQ_Events[IRQ_NU].irqpHandle(); NVIC->ICPR[1] |= (1<<IRQ_NU); break; case ADC1_IRQn: ADC1->SR &= ~(1<<0); IRQ_Events[IRQ_NU].irqpHandle(); NVIC->ICPR[1] |= (1<<IRQ_NU); break; } }
说明:并不是每一个中断都需要解挂,置1,这个需要看用户手册,对应的中断解释,这里,我对中断时间进行封装,就是前面所说的函数指针,IRQ_Events[IRQ_NU].irqpHandle();这一步,就是在执行中断产生之后,需要做的事。封装了一个结构体。
三、收获与意义
1、收获与意义:总的来说,现在的ARM开发,都是调用库函数,很多时候,我们并不知道他的流程是怎么样的,那个使用起来也是懵懵的,寄存器实现,就是完整的从底层学起,虽然是很麻烦,还需要去看流程图,配置哪些寄存器,但是,这个学懂之后,去使用库函数就会更加明白,那些库函数,内部其实就是这样实现的,所以,有必要学习一下这个,这样也可以自己去封装。
源码下载
1、下载地址:百度网盘:提取码:w0rs
本文地址:https://blog.csdn.net/qq_42411190/article/details/107897002