完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
一、实验介绍 1. 试验器材介绍
2. 实验理论介绍 芯片的地址映射和寄存器映射原理 存储器映射 存储器在产家制作完成后是一片没有任何信息的物理存储器,而CPU要进行访存就涉及到内存地址的概念,因此存储器映射就是为物理内存按一定编码规则分配地址的行为。值得注意,存储器映射一般是由产家规定,用户不能随意更改。 寄存器映射 寄存器映射是在存储器映射的基础上进行的。 以STM32为例,操作硬件本质上就是操作寄存器。在存储器片上外设区域,四字节为一个单元,每个单元对应不同的功能。当我们控制这些单元时就可以驱动外设工作,我们可以找到每个单元的起始地址,然后通过C 语言指针的操作方式来访问这些单元。但若每次都是通过这种方式访问地址,不好记忆且易出错。这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名实质上就是寄存器名字。给已分配好地址(通过存储器映射实现)的有特定功能的内存单元取别名的过程就叫寄存器映射。 如何找到芯片中寄存器的地址 如果想要找到芯片中某个寄存器的地址的话,就需要查看数据手册,并通过一定的计算,就可以找到寄存器的地址啦。 寄存器的地址分为基地址和偏移地址 GPIO口初始化设置 GPIO口介绍 每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH), 两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器 根据数据手册中列出的每个I/O端口的特定硬件特征,GPIO端口的每个位可以由软件分别配置成多种模式。
GPIO口在哪里 参考芯片手册,在stm32f103c8t6中有A B C D E F G七个GPIO口,由上文提到的地址计算方法,可计算出寄存器地址,在通过储存器原理图可找到接口。 关于GPIO的更多地址资料可参考芯片的数据手册 (下载资料都以网盘的形式放在了文末) 初始化步骤 第一步:使能GPIOx口的时钟 第二步:指明GPIOx口的哪一位,这一位的速度大小以及模式。 第三步:调用GPIOx口初始化函数,进行初始化。 第四步:调用GPIO-SetBits函数,进行相应为的置位。 这篇博客中有详尽的关于GPIO初始化的实例与代码,供大家参考 二、实验操作 实验操作分三部分
其实网上的流水灯代码资料是比较多的,所以我可能对于代码的讲解不会很详尽,对于硬件接线一些上手操作可能会细点。 1. C语言库函数版 环境配置(库函数之类的东西) 这个里面涉及到了一个keil模块化编程的事,说着比较麻烦,但是学会了又很简单,建议百度找找其他博客学一下。 C语言源码 此程序是将A5 A6 A7作为输出口的 #include "stm32f10x.h" void Delay(u32 count) //延迟函数,参数为设置的延迟时间(不准确,具体时间可由keil的示波器观察) { u32 i=0; for(;i } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; //设置PA5、PA6、PA7为输出口 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7; //GPIO口的初始化操作 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_SetBits(GPIOA,GPIO_Pin_5); //初始化GPIO为高电平,LED低电平亮,高电平灭 GPIO_SetBits(GPIOA,GPIO_Pin_6); GPIO_SetBits(GPIOA,GPIO_Pin_7); while(1) //永真循环,进行不断的流水灯循环闪烁 { GPIO_ResetBits(GPIOA,GPIO_Pin_5); GPIO_SetBits(GPIOA,GPIO_Pin_6); GPIO_SetBits(GPIOA,GPIO_Pin_7); Delay(4600000);Delay(4600000);Delay(4600000); //流水灯中,由延时函数控制时间间隔 GPIO_SetBits(GPIOA,GPIO_Pin_5); GPIO_ResetBits(GPIOA,GPIO_Pin_6); GPIO_SetBits(GPIOA,GPIO_Pin_7); Delay(4600000);Delay(4600000);Delay(4600000); GPIO_SetBits(GPIOA,GPIO_Pin_5); GPIO_SetBits(GPIOA,GPIO_Pin_6); GPIO_ResetBits(GPIOA,GPIO_Pin_7); Delay(4600000);Delay(4600000);Delay(4600000); } } 接线
编译程序
结果观察 可以观察到三个led灯依次闪烁,时间间隔约为一秒 2. 汇编语言版 汇编语言我不会,学着太麻烦了,所以参考了另外一位博主的代码,尝试理解了一下,并把它的代码烧入了试试,也是可行的,相当于是验证性实验了,代码写得很详细。 注意:此代码是将PA7,PB9,PC15作为输出的,与上个C语言代码不同 RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,时钟,0x40021018为时钟地址 GPIOC_CRH EQU 0x40011004;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址为0x04 GPIOC_ORD EQU 0x4001100c;配置GPIOC_ORD寄存器,是端口输出寄存器,输出由这里控制 GPIOA_CRL EQU 0x40010800;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址为0x04 GPIOA_ORD EQU 0x4001080C;配置GPIOC_ORD寄存器,是端口输出寄存器,输出由这里控制 GPIOB_CRH EQU 0x40010C04;配置GPIOC_CRH寄存器,是端口配置高寄存器,高位的偏移地址为0x04 GPIOB_ORD EQU 0x40010C0C;配置GPIOC_ORD寄存器,是端口输出寄存器,输出由这里控制 Stack_Size EQU 0x00000400;栈的大小 ;分配一个stack段,该段不初始化,可读写,按8字节对齐。分配一个大小为Stack_Size的存储空间,并使栈顶的地址为__initial_sp。 AREA STACK, NOINIT, READWRITE, ALIGN=3 ;NOINIT: = NO Init,不初始化。READWRITE : 可读,可写。ALIGN =3 : 2^3 对齐,即8字节对齐。 Stack_Mem SPACE Stack_Size __initial_sp AREA RESET, DATA, READONLY __Vectors DCD __initial_sp ; Top of Stack DCD Reset_Handler ; Reset Handler AREA |.text|, CODE, READONLY THUMB REQUIRE8 PRESERVE8 ENTRY Reset_Handler bl LED_Init;bl:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器 MainLoop BL LED_ON_C BL Delay BL LED_OFF_C BL Delay BL LED_ON_A BL Delay BL LED_OFF_A BL Delay BL LED_ON_B BL Delay BL LED_OFF_B BL Delay B MainLoop;B:无条件跳转。 LED_Init;LED初始化 PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈 LDR R0,=RCC_APB2ENR;LDR是把地址装载到寄存器中(比如R0)。 ORR R0,R0,#0x1c;ORR 按位或操作,11100将R0的第二位置1,其他位不变 LDR R1,=RCC_APB2ENR STR R0,[R1];STR是把值存储到寄存器所指的地址中,将r0里存储的值给rcc寄存器 ;上面一部分汇编代码是控制时钟的 ;初始化GPIOA部分 LDR R0,=GPIOA_CRL BIC R0,R0,#0x0fffffff;BIC 先把立即数取反,再按位与 LDR R1,=GPIOA_CRL STR R0,[R1] ;上面的代码是初始化CPIOC_CRH LDR R0,=GPIOA_CRL ORR R0,#0x20000000;开启的是pc15,所以是2,为0100,是推挽输出模式,最大速度为2mhz LDR R1,=GPIOA_CRL STR R0,[R1] ;GPIOC的端口配置高寄存器配置完毕,也就是CPIOA_CRH配置完成,端口的输出模式确定,不使用的都设为复位后的状态,为0100,所以上面处理输出为都是4 ;将PC15置1 MOV R0,#0x80; 二进制为0b1000 0000 ,第7位就是a7引脚的输出电压 LDR R1,=GPIOA_ORD ;由r1控制ord寄存器 STR R0,[R1] ;将ord寄存器的值变为r0的值 ;初始化GPIOB部分 LDR R0,=GPIOB_CRH BIC R0,R0,#0xffffff0f;BIC 先把立即数取反,再按位与,用的是b9,所以把第二位置零 LDR R1,=GPIOB_CRH STR R0,[R1] ;上面的代码是初始化CPIOC_CRH LDR R0,=GPIOB_CRH ORR R0,#0x00000020;开启的是pc15,所以是2,为0100,是推挽输出模式,最大速度为2mhz LDR R1,=GPIOB_CRH STR R0,[R1] ;GPIOC的端口配置高寄存器配置完毕,也就是CPIOA_CRH配置完成,端口的输出模式确定,不使用的都设为复位后的状态,为0100,所以上面处理输出为都是4 ;将PC15置1 MOV R0,#0x200; 二进制为0b10 0000 0000,第16位就是b9引脚的输出电压 LDR R1,=GPIOB_ORD ;由r1控制ord寄存器 STR R0,[R1] ;将ord寄存器的值变为r0的值 ;初始化GPIOC部分 LDR R0,=GPIOC_CRH BIC R0,R0,#0x0fffffff;BIC 先把立即数取反,再按位与,就是将三十二位全部置零 LDR R1,=GPIOC_CRH STR R0,[R1] ;上面的代码是初始化CPIOC_CRH LDR R0,=GPIOC_CRH ORR R0,#0x20000000;开启的是pc15,所以是2,为0100,是推挽输出模式,最大速度为2mhz LDR R1,=GPIOC_CRH STR R0,[R1] ;GPIOC的端口配置高寄存器配置完毕,也就是CPIOA_CRH配置完成,端口的输出模式确定,不使用的都设为复位后的状态,为0100,所以上面处理输出为都是4 ;将PC15置1 MOV R0,#0x8000; 二进制为0b1000 0000 0000 0000,第16位就是c15引脚的输出电压 LDR R1,=GPIOC_ORD ;由r1控制ord寄存器 STR R0,[R1] ;将ord寄存器的值变为r0的值 POP {R0,R1,PC};将栈中之前存的R0,R1,LR的值返还给R0,R1,PC LED_ON_A;亮灯 PUSH {R0,R1, LR} MOV R0,#0x00 ;二进制为0b0000 0000 0000 0000,第16位为0,后面将作为pc15的输出电压 LDR R1,=GPIOA_ORD ;将GPIOC的地址赋予r1 STR R0,[R1];将r0的值赋予在GPIOC_ORD中 POP {R0,R1,PC} LED_OFF_A;熄灯 PUSH {R0,R1, LR} MOV R0,#0x80 ;二进制为0b 1000 0000 0000 0000,第16位为1,后面将作为pc15的输出电压 LDR R1,=GPIOA_ORD ;将GPIOC的地址赋予r1 STR R0,[R1] ;[]是指对里面的地址操作,所以是将r0的值赋予GPIOC_ORD POP {R0,R1,PC} LED_ON_B;亮灯 PUSH {R0,R1, LR} MOV R0,#0x000 ;二进制为0b0000 0000 0000 0000,第16位为0,后面将作为pc15的输出电压 LDR R1,=GPIOB_ORD ;将GPIOC的地址赋予r1 STR R0,[R1];将r0的值赋予在GPIOC_ORD中 POP {R0,R1,PC} LED_OFF_B;熄灯 PUSH {R0,R1, LR} MOV R0,#0x200 ;二进制为0b 1000 0000 0000 0000,第16位为1,后面将作为pc15的输出电压 LDR R1,=GPIOB_ORD ;将GPIOC的地址赋予r1 STR R0,[R1] ;[]是指对里面的地址操作,所以是将r0的值赋予GPIOC_ORD POP {R0,R1,PC} LED_ON_C;亮灯 PUSH {R0,R1, LR} MOV R0,#0x0000 ;二进制为0b0000 0000 0000 0000,第16位为0,后面将作为pc15的输出电压 LDR R1,=GPIOC_ORD ;将GPIOC的地址赋予r1 STR R0,[R1];将r0的值赋予在GPIOC_ORD中 POP {R0,R1,PC} LED_OFF_C;熄灯 PUSH {R0,R1, LR} MOV R0,#0x8000 ;二进制为0b 1000 0000 0000 0000,第16位为1,后面将作为pc15的输出电压 LDR R1,=GPIOC_ORD ;将GPIOC的地址赋予r1 STR R0,[R1] ;[]是指对里面的地址操作,所以是将r0的值赋予GPIOC_ORD POP {R0,R1,PC} Delay PUSH {R0,R1, LR} MOVS R0,#0 MOVS R1,#0 MOVS R2,#0 DelayLoop0 ADDS R0,R0,#1 CMP R0,#330 BCC DelayLoop0 MOVS R0,#0 ADDS R1,R1,#1 CMP R1,#330 BCC DelayLoop0 ;无进位 MOVS R0,#0 MOVS R1,#0 ADDS R2,R2,#1 CMP R2,#15 BCC DelayLoop0 POP {R0,R1,PC} NOP END 结果观察 三、总结 本次实验用C语言和汇编语言的方式实现了,主要是练习了如何翻看芯片手册来获取想要的信息,也从一定程度上掌握了程序的烧入、调试方法,这也是自己的第一次硬件尝试,终于是迈入了第一只脚进去,以前一直对这个东西有种恐惧,做完了之后也发现没有那么难,也产生了点兴趣。 |
|
|
|
只有小组成员才能发言,加入小组>>
3308 浏览 9 评论
2988 浏览 16 评论
3490 浏览 1 评论
9049 浏览 16 评论
4083 浏览 18 评论
1167浏览 3评论
601浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
592浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2329浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1892浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-18 10:16 , Processed in 1.053545 second(s), Total 50, Slave 40 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号