/**新建工程模板
建库之点亮LED灯
**/
#include "stm32f4xx.h"
//总线
#define PERIPH_BASE ((unsigned int)0x40000000)
#define AHB1PERIPH_BASE (PERIPH_BASE+0x00020000)
#define GPIOH_BASE (AHBPERIPH_BASE+0x00001c00)
#define GPIOH_MODER *(unsigned int*)(GPIOH_BASE+0x00)
#define GPIOH_ODR *(unsigned int*)(GPIOH_BASE+0x14)
#define RCC_BASE (AHB1PERIPH_BASE+0X00003800)
#define RCC_AHB1ENR *(unsigned int *)(RCC_BASE+0x30)
int main(void)
{
RCCAHB1PERIPH_BASE |= (1<<7);
GPIOH_MODER &= ~(3<<2*10);
GPIOH_MODER |= (1<<2*10);
//PH10输出低电平
GPIOH_ODR &= ~(1<<10);
while(1);
}
void SystemInit(void)
{
}
下面是我的一堆乱七八糟的解析:
/**新建工程模板
建库之点亮LED灯
**/
#include "stm32f4xx.h"
//总线
#define PERIPH_BASE ((unsigned int)0x40000000)
#define AHB1PERIPH_BASE (PERIPH_BASE+0x00020000) //AHB1PERIPH_BASE基于总线偏移
#define GPIOH_BASE (AHBPERIPH_BASE+0x00001c00) //GPIOH_BASE基于AHB1总线偏移
#define GPIOH_MODER *(unsigned int*)(GPIOH_BASE+0x00) //GPIOH_MODER寄存器基于GPIOH基地址的偏移//(unsigned int*)(GPIOH_BASE+0x00)强制指针转换,意义:将该整数转化为一个地址
#define GPIOH_ODR *(unsigned int*)(GPIOH_BASE+0x14) //括号外的*号可以放在第16行,17,20行的寄存器前面//*GPIOH_ODR = (unsigned int*)(GPIOH_BASE+0x14)
//外设基地址 :PERIPH_BASE ->总线基地址:APB1PERIPH_BASE ->GPIO外设基地址: GPIOA_BASE ->寄存器基地址 :GPIOH_ODR
#define RCC_BASE (AHB1PERIPH_BASE+0X00003800)
#define RCC_AHB1ENR *(unsigned int *)(RCC_BASE+0x30) //访问一个绝对地址把一个整型数强制转换为一个指针是合法的
//*(int *)(0x67a9) = 0xaa55; 或者这样写:
//int *ptr; ptr = (int *)0x67a9;
int main(void)
{
RCCAHB1PERIPH_BASE |= (1<<7);
//STM32为了降低功耗,刚开始上电复位时,每个外设始终都是关闭的
//PH10配置为输出
//Q1:清零 ?ANS:与零与
//Q2:置1 ? ANS:与1或 :如果是两位,则操作受原来该位数据影响(假如两位为10,想要变成01,那么单纯的10与01相或不能时结果变为01.),所以为了实现置1操作,先清零,再与1或。
//总结:
将寄存器的某一位eg:GPIOH_ODR清零,与该位与0相与GPIOH_ODR &= ~(1<<10),GPIOH_ODR置1,该位与1相或GPIOH_ODR |= (1<<10)。
将寄存器的某两位eg:GPIOH_MODER变为00时,即清零型, 将该两位与11或之后再取反
// 01时,即置1型, 将该两位清零GPIOH_MODER &= ~(3<<2*10),再与1左移X位的值相或。
// 10时,即置1型, 将该两位清零GPIOH_MODER &= ~(3<<2*10),再与1左移X位的值相或。
// 11时,即置1型, 将该两位清零GPIOH_MODER &= ~(3<<2*10),再与3左移X位的值相或。
GPIOH_MODER &= ~(3<<2*10);
GPIOH_MODER |= (1<<2*10);
//PH10输出低电平
GPIOH_ODR &= ~(1<<10);
while(1);
}
void SystemInit(void)
{
}
// 逻辑左移时,最高位丢失,最低位补0;
// 逻辑右移时,最高位补0,最低位丢失;
// 算术左移时,依次左移一位,尾部补0,最高的符号位保持不变。
// 算术右移时,依次右移一位,尾部丢失,符号位右移后,原位置上复制一个符号位;
// 循环左移时,将最高位重新放置最低位
// 循环右移时,将最低位重新放置最高位
// 1010100010101 逻辑左移一位结果为 0101000101010
// 逻辑右移一位结果为 0101010001010
// 算术左移一位结果为 1101000101010
// 算术右移一位结果为 1101010001010
// 循环左移一位结果为 0101000101011
// 循环右移一位结果为 1101010001010
//关于双重指针的问题:
*(unsigned int*)(0x4002 1C14) = 0xFFFF;
0x4002 1C14 在我们看来是 GPIOH 端口 ODR 的地址,但是在编译器看来,这只是一
个普通的变量,是一个立即数,要想让编译器也认为是指针,我们得进行强制类型转换,
把它转换成指针,即(unsigned int *)0x4002 1C14,然后再对这个指针进行 * 操作
/* 控制 GPIOH 引脚 10 输出低电平(BSRR 寄存器的 BR10 置 1) */
*(unsigned int *)GPIOH_BSRR |= (0x01<<(16+10));
/* 控制 GPIOH 引脚 10 输出高电平(BSRR 寄存器的 BS10 置 1) */
*(unsigned int *)GPIOH_BSRR |= 0x01<<10;
unsigned int temp;
/* 控制 GPIOH 端口所有引脚的电平(读 IDR 寄存器) */
temp = *(unsigned int *)GPIOH_IDR;
使用 (unsigned int *) 把 GPIOH_BSRR 宏的数值强制转换成了地址,然后再用
“*”号做取指针操作,对该地址的赋值,从而实现了写寄存器的功能。同样,读寄存器也
是用取指针操作,把寄存器中的数据取到变量里,从而获取 STM32 外设的状态
上一篇:STM32 LED灯的另一种写法
下一篇:STM32CubeMX下按键模块控制PWM蜂鸣器模块
推荐阅读最新更新时间:2024-11-06 20:05
设计资源 培训 开发板 精华推荐
- ESP32-M1 Reach Out:开放式硬件 ESP32-M1 开发板,Wi-Fi 范围可达 1.2 公里(原理图等)
- NSI45060JDT4G 大电流 LED 灯串的典型应用
- AD8506ARMZ四极巴特沃斯滤波器在血糖仪中的典型应用电路
- LTC1863LIGN 微功率、3V、12 位、8 通道、175ksps ADC 的典型应用电路
- 6418电子管式耳放
- LT1317BCS8 单节锂离子电池至 4V/70mA、-4V/10mA 的典型应用电路
- 用于便携式的 1.2V、1.8V DC 到 DC 多输出电源
- 用于工业控制的 8W、25V 交流转直流单路输出电源
- LTC2941-1 的典型应用 - 具有内部检测电阻器的 1A I2C 电池电量计
- EVB-USB2240-IND,使用 USB2240 的评估板是具有安全数字 SD 的超快速 USB 2.0 多格式闪存媒体控制器