小学生
最后登录1970-1-1
在线时间 小时
注册时间2021-7-22
|
#include "stm32f10x.h"
#define uchar unsigned char
#define uint unsigned int
void Mfdelay(uchar n)//模糊延时
{
while(n--)
{
for(unsigned int delay=0;delay<20000;delay++);
}
}
void first()//点亮一个LED
{
*(uint * )0x40010C0C&=~(1<<0);//通过ODR给PB0写一个数据0
//*(uint * )0x40010C0C&=~(1<<1);//通过ODR给PB0写一个数据0
//*(uint * )0x40010C0C&=~(1<<5);//通过ODR给PB0写一个数据0
/*
我们根据0X0000 000C(GPIOB地址)此地址找到端口输出数据ODR寄存器为什么加上0X0000 000C呢?
是因为GPIOB上的ODR寄存器地址偏移为0CH也就是0X0000 000C!所以我们在GPIOB的地址上加上这个偏移量即可!
*/
/*
32和51不同,32还需要告诉单片机寄存器中的数据是输入还是输出,以及其对应的模式!
点亮LED灯我们采用通用推挽输出模式,输出速度配置为50MHZ
详细如下:
这是一组 CNF0[1:0] MODE0[1:0]
CNFy[1:0]:端口x配置位(y = 0…7) (Port x configuration bits)
软件通过这些位配置相应的I/O端口,请参考表17端口位配置表。
在输入模式(MODE[1:0]=00):
00:模拟输入模式
01:浮空输入模式(复位后的状态)
10:上拉/下拉输入模式
11:保留
在输出模式(MODE[1:0]>00):
00:通用推挽输出模式
01:通用开漏输出模式
10:复用功能推挽输出模式
11:复用功能开漏输出模式
MODEy[1:0]:端口x的模式位(y = 0…7) (Port x mode bits)
软件通过这些位配置相应的I/O端口,请参考表17端口位配置表。
00:输入模式(复位后的状态)
01:输出模式,最大速度10MHz
10:输出模式,最大速度2MHz
11:输出模式,最大速度50MHz
低八位采用端口配置低寄存器GPIOx_CRL,高八位端口配置高寄存器GPIOx_CRH!
*/
*(uint * )0x40010C00|=((1)<<(4*0));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
//*(uint * )0x40010C00|=((1)<<(4*1));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
//*(uint * )0x40010C00|=((1)<<(4*5));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
*(uint * )0X40021018|=(1)<<3;//打开GPIOB端口的时钟
/*
问题:
先将这个问题之前的代码烧入单片机灯已经亮了,如果将ODR此时注视掉,灯还会保持亮吗?
答案:
会亮的,因为ODR自动复位它里面的最低位已经是0了
*/
}
void second()//三个LED一起点亮
{
RCC_APB2ENR|=((1)<<(3));//打开GPIOB端口的时钟
GPIOB_CRL&=~((0xF)<<(4*0));//将PBO的输出配置数据清空清零
GPIOB_CRL|=((1)<<(4*0));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
GPIOB_CRL&=~((0xF)<<(4*1));//将PB1的输出配置数据清空清零
GPIOB_CRL|=((1)<<(4*1));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
GPIOB_CRL&=~((0xF)<<(4*5));//将PB5的输出配置数据清空清零
GPIOB_CRL|=((1)<<(4*5));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
GPIOB_ODR&=~(1<<0);//通过ODR给PB0写一个数据0并输出 绿色
GPIOB_ODR&=~(1<<1);//通过ODR给PB1写一个数据0并输出 蓝色
GPIOB_ODR&=~(1<<5);//通过ODR给PB5写一个数据0并输出 红色
}
void thirdly()//流水灯
{
RCC_APB2ENR|=((1)<<(3));//打开GPIOB端口的时钟
uchar a[]={0,1,5};
while(1)
{
for(uchar n=0;n<3;n++)
{
GPIOB_CRL&=~((0xF)<<(4*a[n]));//将PBy的输出配置数据清空清零
GPIOB_CRL|=((1)<<(4*a[n]));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
switch(a[n])
{
case 0:
GPIOB_ODR&=~(1<<0);//通过ODR给PB0写一个数据0并输出 绿色
GPIOB_ODR|=(1<<1);//通过ODR给PB1写一个数据1并输出 蓝色
GPIOB_ODR|=(1<<5);//通过ODR给PB5写一个数据1并输出 红色
break;
case 1:
GPIOB_ODR|=(1<<0);//通过ODR给PB0写一个数据1并输出 绿色
GPIOB_ODR&=~(1<<1);//通过ODR给PB1写一个数据0并输出 蓝色
GPIOB_ODR|=(1<<5);//通过ODR给PB5写一个数据1并输出 红色
break;
case 5:
GPIOB_ODR|=(1<<0);//通过ODR给PB0写一个数据0并输出 绿色
GPIOB_ODR|=(1<<1);//通过ODR给PB1写一个数据0并输出 蓝色
GPIOB_ODR&=~(1<<5);//通过ODR给PB5写一个数据1并输出 红色
break;
}
Mfdelay(50);
/*
模糊延时多久我也不知道多久,关于这个流水灯颜色顺序可以参考随机数做成
三色比的RGB彩光!但是刚刚入门不知道随机数如何望大佬指点下方向!
*/
}
}
}
void mffirst()//麻烦且第一(狗头保命!)
{
/*首先我们试着点亮PB0吧,新手上路稳一点避免翻车!*/
GPIOB_BSRR=((1)<<(0));//灭
GPIOB_BSRR=((0)<<(0));
GPIOB_BSRR|=((1)<<(16));
/*
以下数据来于手册:
BSRR端口设置/清除寄存器的高16位是写1的话会将其对应的ODR那边的位置于0!
我们先拿PBO举列,我们需要将PB0点亮我使用的是F103系列的指南针开发板需要将
PB0置于低电平。接下来就是我们熟悉的打开GPIOB的时钟以及配置相对应的输出数据
*/
RCC_APB2ENR|=((1)<<(3));//打开GPIOB端口的时钟
GPIOB_CRL&=~((0xF)<<(4*0));//将PBO的输出配置数据清空清零
GPIOB_CRL|=((1)<<(4*0));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
/*OK,作业完成但既然都写了那就在啰嗦几句做个BSRR和BRR版本的流水灯*/
}
void mfsecond()//麻烦且第二
{
/*老思路,单一LED亮的前提是——别家都歇菜!*/
RCC_APB2ENR|=((1)<<(3));//打开GPIOB端口的时钟
uchar a[]={0,1,5};
while(1)
{
for(uchar n=0;n<3;n++)
{
for(uchar js=0;js<3;js++)
{
GPIOB_BSRR=((1)<<(a[js]));//灭
}
GPIOB_CRL&=~((0xF)<<(4*a[n]));//将PBy的输出配置数据清空清零
GPIOB_CRL|=((1)<<(4*a[n]));//采用通用推挽输出模式,输出速度配置为10MHZ 0001
switch(a[n])
{
case 0://绿
GPIOB_BSRR=((0)<<(0));
GPIOB_BSRR|=((1)<<(16));
break;
case 1://蓝
GPIOB_BSRR=((0)<<(1));
GPIOB_BSRR|=((1)<<(17));
break;
case 5://红
GPIOB_BSRR=((0)<<(5));
GPIOB_BSRR|=((1)<<(21));
break;
}
Mfdelay(50);
}
}
}
int main(void)
{
//first();(我选择了单独点亮绿色根据个人修改实验)
//second();//三个LED一起点亮(白色有点小亮)
//thirdly();//流水灯(绿蓝红可根据自己需求改变顺序)
/*以上是通过端口输出寄存器ODR来实现其功能接下来将使用BSRR和BRR寄存器完成,虽然我估计我实际用的时候不太可能鸟这两玩意目前来看*/
//mffirst();
mfsecond();//麻烦且第二
}
/*
本次作业如下:已经全部完成(72MHZ,HSI=8MHZ)
1、点亮其他的两个LED
2、写一个简单的延时函数
3.实现GPIO其他寄存器的映射
4.采用操作BSRR、BRR来编程点亮OLED灯
*/
void SystemInit()
{
//函数体为空!目的骗过编译器不报错!
/*
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
这是启动文件中的一部分汇编代码这里的意思是先有个初始化时钟的函数(这个函数是在库函数的)在去执行main.c的内容
但是我们这是寄存器并不是库函数所以我们做了一个空的初始化时钟的函数来骗过编译器!
*/
}
//用来写STM32寄存器映射的代码
//外设 PERIPH
//外设基地址
#define PERIPH_BASE ((unsigned int )0x40000000)
//APB1的基地址
#define APB1PERIPH_BASE PERIPH_BASE
//通过外设基地址推出APB2的基地址
#define APB2PERIPH_BASE (PERIPH_BASE+0X10000)
//通过外设基地址推出AHB的基地址
#define AHBPERIPH_BASE (PERIPH_BASE + 0x18000)
总的来说还是可以再接再厉!giao!
//RCC的基地址
#define RCC_BASE (AHBPERIPH_BASE+0x9000)
//GPIOB的基地址
#define GPIOB_BASE (APB2PERIPH_BASE+0x0C00)
/*
为什么要这样写呢?
答案:
因为我们到时候是希望直接操作RCC_APB2ENR不然到时候在指针那些不美观!
我们首先推出RCC_APB2ENR的及地址RCC_BASE+0X18
如果我们想操作一个4字节的地址应该怎么做?当然是用32位的指针来操作了(unsigned int*)(RCC_BASE+0X18)
接下来我们将取内容也就是操作内存空间这一步也放入定义里面*(unsigned int*)(RCC_BASE+0X18)
*/
//APB2外设时钟RCC_APB2ENR的基地址
#define RCC_APB2ENR *(unsigned int*)(RCC_BASE+0X18)
//端口配置低寄存器GPIOB_CRL的基地址
#define GPIOB_CRL *(unsigned int*)(GPIOB_BASE+0X00)
//端口配置高寄存器GPIOB_CRH的基地址
#define GPIOB_CRH *(unsigned int*)(GPIOB_BASE+0X04)
//端口输出寄存器GPIOB_ODR基地址
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0X0C)
//端口设置/清除寄存器BSRR
#define GPIOB_BSRR *(unsigned int*)(GPIOB_BASE+0X10)
//端口清除寄存器BRR
#define GPIOB_BRR *(unsigned int*)(GPIOB_BASE+0X14)
|
-
|