C语言和汇编实现STM32F103点亮LED

目录

  • 一、芯片准备
    • 1)芯片选取STM32F103C8T6
    • 2)点亮流水灯原理
  • 二、C语言点亮
    • 1)keil创建项目及配置
    • 2)配置环境
    • 3)魔法棒配置
    • 4)编译
    • 5)最后成功结果
  • 三、汇编点亮
  • 四、总结
  • 五、参考链接
  • 六、需要的软件

一、芯片准备

1)芯片选取STM32F103C8T6

材料:STM32最小系统核心板(STM32F103C8T6)+面包板+3只红绿黄LED 搭建电路,使用GPIOB、GPIOC、GPIOD这3个端口控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1.34s(这里的间隔时长是实验所得)。

STM32F103C8T6是一款基于ARM Cortex-M 内核STM32系列的32位的微控制器,程序存储器容量是64KB,需要电压2V~3.6V,工作温度为-40°C ~ 85°C。

2)点亮流水灯原理

寄存器可以存储数据,指令,也可以担任一些特定的功能,stm32板子里由很多寄存器,如果想实现流水灯操作,就需要对相应的引脚进行操作,想对引脚进行操作,就需要对相应的引脚进行时钟使能配置、端口配置(高或低)寄存器配置、端口输出寄存器配置。

1.因为流水灯要操作的引脚都是在GPIO端口的,所以根据系统结构图,属于AHB总线,所以所要用的端口的复位和时间控制都受RCC控制。
C语言和汇编实现STM32F103点亮LED_第1张图片

2.再看寄存器组起始地址表,可以看到RCC的地址范围,且可以看到要控制的寄存器(我的板子上只有ABC三种)都是在APB2总线。
C语言和汇编实现STM32F103点亮LED_第2张图片

3.跳到这里,就是外设时钟使能寄存器,,偏移量为0x18,而在前面一个表可以看到起始地址为0x4002 1000,偏移量为0x18,所以该寄存器的地址为0x4002 1018
C语言和汇编实现STM32F103点亮LED_第3张图片

4.图中圈处理就是该寄存器里各位的含义,比如第三位也就是2那个位置为1时,就是GPIOA的时钟开启了。这时我们就可以用代码表达出来了,以PA7引脚为例:

#define RCC_AP2ENR	*((unsigned volatile int*)0x40021018) #时钟使能寄存器
RCC_AP2ENR|=1<<2;			//开启APB2-GPIOA外设时钟使能

5.接下来就是配置端口配置寄存器了,可以发现上面的时钟使能寄存器开启时钟是针对一个区域的,并不能确定引脚,而这个寄存器就是确定引脚的,端口配置寄存器有两个,分别为端口配置低寄存器(CRL)和端口配置高寄存器(CRH),每四位配置一个端口,如11 、01,11就是选择开启功能,01就是选择模式和确定最大速度,但有一点不一样,低寄存器的偏移地址为0x00,高寄存器的偏移地址为0x04。

低寄存器:
C语言和汇编实现STM32F103点亮LED_第4张图片
高寄存器:
C语言和汇编实现STM32F103点亮LED_第5张图片

6.以PA7为示例,相应端口配置器GPIOA_CRL地址为GPIOA的基址+上偏移量,为0x40010800,而这个端口要开启,所以要使对应位为相应的值,我这里是0x20000000,设置推挽输出并设置最大速度为2Mhz,下面为相应代码:

#define GPIOA_CRL	*((unsigned volatile int*)0x40010800)
GPIOA_CRL=0x20000000;		//PA7推挽输出,2Mhz

7.接下来就是配置端口输出寄存器(ORD),可以看到偏移量为0xc,所以该寄存器的地址等于端口的基址加上偏移量,在相应的位赋值可以控制输出电压,0为低电压,1为高电压,以PA7引脚为例子,想要输出高电压,就需要在第八位赋1。
C语言和汇编实现STM32F103点亮LED_第6张图片

代码如下:

#define	GPIOA_ORD	*((unsigned volatile int*)0x4001080C)
GPIOA_ORD|=1<<7;			//设置初始灯为亮

8.这里就可以控制led亮或者灭了,实现流水灯只需增加灯的数量和增加一些延时就行了。

二、C语言点亮

1)keil创建项目及配置

1.打开keil,Project->new μVision Project:
C语言和汇编实现STM32F103点亮LED_第7张图片
2、设置项目工程的路径和名称,点击保存
C语言和汇编实现STM32F103点亮LED_第8张图片

2)配置环境

1、设置工程的目标环境,本实验基于STM32F103C8T6,因此在弹出的窗口选择相应的选项,这里没有STM32F103C8T6,所以选择STM32F103C8,点击保存即可:
C语言和汇编实现STM32F103点亮LED_第9张图片2、ARM的CMSIS已经把开发所需要的软件组件都封装好了,因此直接选择即可:
C语言和汇编实现STM32F103点亮LED_第10张图片
3、鼠标右键单击Source Group 1,具体如下图所示:
C语言和汇编实现STM32F103点亮LED_第11张图片
C语言和汇编实现STM32F103点亮LED_第12张图片
最后add,创建文件完成!

4.将代码粘贴进.c文件里:

//--------------APB2使能时钟寄存器------------------------
#define RCC_AP2ENR	*((unsigned volatile int*)0x40021018)
	//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL	*((unsigned volatile int*)0x40010800)
#define	GPIOA_ORD	*((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH	*((unsigned volatile int*)0x40010C04)
#define	GPIOB_ORD	*((unsigned volatile int*)0x40010C0C)
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH	*((unsigned volatile int*)0x40011004)
#define	GPIOC_ORD	*((unsigned volatile int*)0x4001100C)
//-------------------简单的延时函数-----------------------
void  Delay_ms( volatile  unsigned  int  t)
{
     unsigned  int  i;
     while(t--)
         for (i=0;i<800;i++);
}
void A_LED_LIGHT(){
	GPIOA_ORD=0x0<<7;		//PA7低电平
	GPIOB_ORD=0x1<<9;		//PB9高电平
	GPIOC_ORD=0x1<<15;		//PC15高电平
}
void B_LED_LIGHT(){
	GPIOA_ORD=0x1<<7;		//PA7高电平
	GPIOB_ORD=0x0<<9;		//PB9低电平
	GPIOC_ORD=0x1<<15;		//PC15高电平
}
void C_LED_LIGHT(){
	GPIOA_ORD=0x1<<7;		//PA7高电平
	GPIOB_ORD=0x1<<9;		//PB9高电平
	GPIOC_ORD=0x0<<15;		//PC15低电平	
}
//------------------------主函数--------------------------
int main()
{
	int j=100;
	RCC_AP2ENR|=1<<2;			//APB2-GPIOA外设时钟使能
	RCC_AP2ENR|=1<<3;			//APB2-GPIOB外设时钟使能	
	RCC_AP2ENR|=1<<4;			//APB2-GPIOC外设时钟使能
	//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<4;
	GPIOA_CRL&=0x0FFFFFFF;		//设置位 清零	
	GPIOA_CRL|=0x20000000;		//PA7推挽输出
	GPIOA_ORD|=1<<7;			//设置PA7初始灯为灭
	
	GPIOB_CRH&=0xFFFFFF0F;		//设置位 清零	
	GPIOB_CRH|=0x00000020;		//PB9推挽输出
	GPIOB_ORD|=1<<9;			//设置初始灯为灭
	
	GPIOC_CRH&=0x0FFFFFFF;		//设置位 清零
	GPIOC_CRH|=0x30000000;   	//PC15推挽输出
	GPIOC_ORD|=0x1<<15;			//设置初始灯为灭	
	while(j)
	{	
		A_LED_LIGHT();	
		Delay_ms(10000000);
		B_LED_LIGHT();
		Delay_ms(10000000);
		C_LED_LIGHT();
		Delay_ms(10000000);
	}
}

3)魔法棒配置

1.打开魔法棒:
C语言和汇编实现STM32F103点亮LED_第13张图片

2.output里勾上create HEX File:
C语言和汇编实现STM32F103点亮LED_第14张图片
3.debug里,将图中改为DARMSTM.DLL和-pSTM32F103C8,OK:
C语言和汇编实现STM32F103点亮LED_第15张图片

4)编译

1.rebuild,会创建一个.hex文件:
C语言和汇编实现STM32F103点亮LED_第16张图片
2.打开mucisp,按下面步骤,1是选择生成的hex文件(需要下载串口,串口下载文件在文章末尾,USB驱动也要下载)
C语言和汇编实现STM32F103点亮LED_第17张图片
连接串口过后,需要将boot0置0,boot1置1,点击reset(板子上的)。
3.点击开始编程,出现右边信息就成功了!
C语言和汇编实现STM32F103点亮LED_第18张图片

5)最后成功结果

三、汇编点亮

1.前面配置差不多,就改两个地方
一个是文件格式,一个是:
C语言和汇编实现STM32F103点亮LED_第19张图片
2.汇编代码

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

3.结果

四、总结

这次实验还是挺复杂的,对stm32芯片各个串口输入输出,工作原理、寄存器地址等有了一定了解,个人掌握的程度不够深,会出现各个环节无法联系的问题,串口要检查连接没有,最没想到的出错就是接电路的一些引脚会接触不良,导致有一个灯会不亮,只能用手扶着。

五、参考链接

STM32F103C8T6百度百科
STM32寄存器的简介、地址查找,与直接操作寄存器
STM32从地址到寄存器
STM32汇编程序及点灯实验

六、需要的软件

STM32串口下载
mcuisp软件下载,提取码:h2xc

你可能感兴趣的:(c语言,stm32,arm)