之前我从宏观上介绍了CM3架构和STM32的相关内容,现在我们就开始正式进行STM32的灵魂塑造---程序编写。为什么说是塑造,因为整个代码是从无到有的而非利用官方库的开发,就好像新生的婴儿呱呱坠地。。。罗嗦止于此,切入正题!
在我的一篇文章《ARM寄存器C操作探讨》里,我曾详细的介绍了ARM通常采用的两种寄存器C操作的方式,现在我们就实际应用一下第二种方法。为了帮初学者形成ARM编程的一般思路和思想,我会从解读Datasheet到实际编程都说明的非常详细。
首先,我们要读Datasheet,这里我选用的芯片是STM32F103RB,大家可以到ST的官网寻找下载,读Datasheet读什么?就像物质是由原子组成,程序是由寄存器的配置组成,尤其对ARM来说,灵活的配置寄存器是基础中的基础,说来容易,可至此还是有种无从下手的感觉,下面我就从寄存器操作开始讲讲构建自己的ARM编程模式。
打开KEIL,新建一个工程,device选择STM32F103RB,详细的我这里不再展开,相信大家在51或其它微处理器的学习中都用的熟的不能再熟,不赘述,直奔核心---寄存器的操作。要操作一个寄存器我们得知道这个寄存器的地址,如何寻址?这就是重点。我们需要查看Datasheet来确定STM32F103RB这个芯片或者说STM32F103系列芯片的寄存器映射。依据这个内存映射,我们可以找到芯片外设的首地址,而这正是我们想要的、需要的。那么具体怎么写?我们新建一个名为MYstm32f10x_map.h的文件,内容可以这么写:
#ifndef MYstm32f10x_map_h_h_
#define MYstm32f10x_map_h_h_
//数据类型定义
typedef unsigned int uint32;
typedef unsigned short int uint16;
//####################################################################################
//****RCC REGS******//
//####################################################################################
typedef struct tagRCC_REGS{
uint32 CR; //时钟控制寄存器
uint32 CFGR; //时钟配置寄存器
uint32 CIR; //时钟中断寄存器
uint32 APB2RSTR; //APB2外设复位寄存器
uint32 APB1RSTR; //APB1外设复位寄存器
uint32 AHBENR; //AHB外设时钟使能寄存器
uint32 APB2ENR; //APB2外设时钟使能寄存器
uint32 APB1ENR; //APB1外设时钟使能寄存器
uint32 BDCR; //备份域控制寄存器
uint32 CSR; //控制/状态寄存器
}RCC_REGS;
#define RCC ((volatile RCC_REGS *)0x40021000) //RCC寄存器组首地址
//####################################################################################
//****GPIO REGS****//
//####################################################################################
typedef struct tagGPI0X_REGS{ //X=A\B\C
uint32 CRL; //端口配置低寄存器
uint32 CRH; //端口配置高寄存器
uint32 IDR; //端口输入数据寄存器
uint32 ODR; //端口输出数据寄存器
uint32 BSRR; //端口位设置/清除寄存器
uint32 BRR; //端口位清除寄存器
uint32 LCKR; //端口配置锁定寄存器
}GPIOX_REGS;
#define GPIOA ((volatile GPIOX_REGS *)0x40010800) //端口A寄存器组首地址
#define GPIOB ((volatile GPIOX_REGS *)0x40010C00) //端口B寄存器组首地址
#define GPIOC ((volatile GPIOX_REGS *)0x40011000) //端口C寄存器组首地址
//####################################################################################
//*****TIME REGS******//
//####################################################################################
typedef struct tagADVANCED_TIMX_REGS{ //高级控制定时器(X)寄存器组
uint32 CR1; //控制寄存器1
uint32 CR2; //控制寄存器2
uint32 SMCR; //从模式控制寄存器
uint32 DIER; //中断使能寄存器
uint32 SR; //状态寄存器
uint32 EGR; //事件产生寄存器
uint32 CCMR1; //捕获/比较模式寄存器1
uint32 CCMR2; //捕获/比较模式寄存器2
uint32 CCER; //捕获/比较使能寄存器
uint32 CNT; //计数器
uint32 PSC; //预分频器
uint32 ARR; //自动装载寄存器
uint32 RCR; //重复计数寄存器
uint32 CCR1; //捕获/比较寄存器1
uint32 CCR2; //捕获/比较寄存器2
uint32 CCR3; //捕获/比较寄存器3
uint32 CCR4; //捕获/比较寄存器4
uint32 BDTR; //刹车和死区寄存器
uint32 DCR; //DMA控制寄存器
uint32 DMAR; //连续模式的DMA地址
}ADVANCED_TIMX_REGS;
typedef struct tagGENERAL_TIMX_REGS{ //X=2-4
uint32 CR1; //控制寄存器1
uint32 CR2; //控制寄存器2
uint32 SMCR; //从模式控制寄存器
uint32 DIER; //DMA/中断使能寄存器
uint32 SR; //状态寄存器
uint32 EGR; //事件产生寄存器
uint32 CCMR1; //捕获/比较模式寄存器1
uint32 CCMR2; //捕获/比较模式寄存器2
uint32 CCER; //捕获/比较使能寄存器
uint32 CNT; //计数器
uint32 PSC; //预分频器
uint32 ARR; //自动装载寄存器
uint32 CCR1; //捕获/比较寄存器1
uint32 CCR2; //捕获/比较寄存器2
uint32 CCR3; //捕获/比较寄存器3
uint32 CCR4; //捕获/比较寄存器4
uint32 DCR; //DMA控制寄存器
uint32 DMAR; //连续模式的DMA地址
}GENERAL_TIMX_REGS;
#define TIM1 ((volatile ADVANCED_TIMX_REGS *) 0x40012C00) //高级控制定时器1寄存器组首地址
#define TIM2 ((volatile GENERAL_TIMX_REGS *) 0x40000000) //通用控制定时器2寄存器组首地址
#define TIM3 ((volatile GENERAL_TIMX_REGS *) 0x40000400) //通用控制定时器3寄存器组首地址
#define TIM4 ((volatile GENERAL_TIMX_REGS *) 0x40000800) //通用控制定时器4寄存器组首地址
//####################################################################################
//*****ADC REGS******//
//####################################################################################
typedef struct tagADCX_REGS{
uint32 SR; //ADC状态寄存器
uint32 CR1; //ADC控制寄存器1
uint32 CR2; //ADC控制寄存器2
uint32 SMPR1; //ADC采样时间寄存器1
uint32 SMPR2; //ADC采样时间寄存器2
uint32 JOFR1; //ADC注入通道数据偏移寄存器1
uint32 JOFR2; //ADC注入通道数据偏移寄存器2
uint32 JOFR3; //ADC注入通道数据偏移寄存器3
uint32 JOFR4; //ADC注入通道数据偏移寄存器4
uint32 HTR; //ADC看门狗高阀值寄存器
uint32 LTR; //ADC看门狗低阀值寄存器
uint32 SQR1; //ADC规则序列寄存器1
uint32 SQR2; //ADC规则序列寄存器2
uint32 SQR3; //ADC规则序列寄存器3
uint32 JSQR; //ADC注入序列寄存器
uint32 JDR1; //ADC 注入数据寄存器1
uint32 JDR2; //ADC 注入数据寄存器2
uint32 JDR3; //ADC 注入数据寄存器3
uint32 JDR4; //ADC 注入数据寄存器4
uint32 DR; //ADC规则数据寄存器
}ADCX_REGS;
#define ADC1 ((volatile ADCX_REGS *) 0x40012400) //ADC1寄存器组首地址
#define ADC2 ((volatile ADCX_REGS *) 0x40012800) //ADC2寄存器组首地址
//等等。。。。。。
#endif
如上,然后我们就可以在.c文件里包含这个头文件,并根据技术手册里的各外设的寄存器描述来读写相应的寄存器,完成想要实现的功能。
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
注意:
可能你在编译过程中会报错,大体意思是说你没有定义某结构体,可我们明明定义了,我想这可能是KEIL的一个BUG(当然也有可能是盗版的缘故~),经过几次猜想验证之后,我发现在typedef struct...这个结构体定义时,在最后的结构体名一定要是你一个字一个字敲进去的,而不是从tagXXX中拷贝而来的,如果是从tagXXX拷贝而来的话就会报上述的错误(真是什么奇怪的事都可能存在~)。
×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××
欢迎大家扫描下方二维码关注我的个人微信公众号,一起交流学习,谢谢。