当前位置: 首页 > news >正文

STM32介绍和GPIO

最小开发板

image-20250625094252947

  • 高速晶振:提供系统高频时钟,保障运行速度。
  • 低速晶振:用于低功耗场景,如实时时钟计时。
  • 高速晶振为 CPU 主频及高速外设提供时钟,确保系统高性能运行,PLL锁相环9次倍频
  • 低速晶振(如 32.768kHz) 专门为 RTC(实时时钟)模块供电,支持低功耗计时,15次分频

STM32F103C8T6

image-20250625095354823

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

VDD + 高电平 1 VCC
VSS - 低电平 0 GND

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

时钟的控制下传输数据

ST-LINK

  • 电脑和单片机无法直接通信
  • USB的电压5V,单片机3.3V
  • ST-LINK,转换电压,解决通信

STM32F103内部框图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

image-20250625110856558

创建第一个项目

点project-new Project 选择一个目录,文件名为Project

image-20250625115452040

选择STM32F103C8

image-20250625115621222

image-20250625120109141

image-20250625120138186

设置:

image-20250625120550322

image-20250625120751849

在项目下建三个目录

image-20250625121047454

image-20250625121329095

image-20250625121351040

image-20250625121459411

image-20250625121704544

image-20250625122036725

补充位的操作

1.一个16进制等于几个二进制位?4位二进制是1位16进制

2.一个字节Byte是8位二进制,所以一个字节需要两位16进制来表示 0xF1——Byte

3.大部分情况下,STM32单片机的寄存器是32位的, 0xFF11FFFF

4.给一个二进制数1100 0011 ,变成1000 0011

寄存器操作PA0

#include "stm32f10x.h"                  // Device headerint main()
{//目标:使用PA0引脚,输出高电平//1.开GPIOA的时钟-GPIOA挂在APB2总线上的,(开APB2上面GPIOA的时钟)--APB2 ENABLE Register//RCC->APB2ENR = 0x00000004;//2.配置这个设备(PA0这个引脚),配置成推挽输出(00),配置的输出速度位50MHz(11),所以是3//GPIOA->CRL = 0x00000003;//3.使用这个设备(PA0这个引脚) output Data register//GPIOA->ODR =0x00000001;//目标:使用PC13引脚,输出低电平//1.开GPIOC的时钟-GPIOC挂在APB2总线上的,(开APB2上面GPIOC的时钟),参考6.3.7RCC->APB2ENR = 0x00000010;//2.配置这个设备(PC13这个引脚),配置成推挽输出(00),配置的输出速度位50MHz(11),所以是3,参考8.2.2GPIOC->CRH = 0x00300000;//3.使用这个设备(PC13这个引脚) GPIOC->ODR =0x00000000;//低电平//GPIOC->ODR =0x00002000;//高电平while(1){}}

库函数操作

#include "stm32f10x.h"                  // Device headervoid delay_ms(int miSec)
{while(miSec--);
}int main()
{//目标:使用PA0引脚,输出高电平//1.开GPIOA的时钟-GPIOA挂在APB2总线上的,(开APB2上面GPIOA的时钟)--APB2 ENABLE RegisterRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//---------------------------------------------------RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);//2. 配置这个设备(PA0这个引脚),配置成推挽输出(00),配置的输出速度位50MHz,GPIO_InitTypeDef GPIO_InitStruct;//定义一个GPIO的结构体GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//模式设置成推挽输出GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;//引脚是0和1GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//电平切换速度50MHzGPIO_Init(GPIOA, &GPIO_InitStruct);//使用结构体配置PA0引脚//-------------------------------------------------GPIO_InitTypeDef GPIO_InitStruct1;//定义一个GPIO的结构体GPIO_InitStruct1.GPIO_Mode = GPIO_Mode_Out_OD;//模式设置成推挽输出GPIO_InitStruct1.GPIO_Pin = GPIO_Pin_13;//引脚是13GPIO_InitStruct1.GPIO_Speed = GPIO_Speed_50MHz;//电平切换速度50MHzGPIO_Init(GPIOC, &GPIO_InitStruct1);//使用结构体配置PC13引脚//3. 使用这个设备(PA0这个引脚)//GPIO_SetBits(GPIOA,GPIO_Pin_0);//GPIOA的Pin0引脚设置成高电平//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//GPIOA的Pin0引脚设置成低电平		//----------------------------------------------------//GPIO_ResetBits(GPIOC,GPIO_Pin_13);//GPIOC的Pin13引脚设置成高电平while(1){// 点亮PA0,熄灭PC13GPIO_SetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1);GPIO_ResetBits(GPIOC, GPIO_Pin_13);delay_ms(500000);  // 延时// 熄灭PA0,点亮PC13GPIO_ResetBits(GPIOA, GPIO_Pin_0 | GPIO_Pin_1);GPIO_SetBits(GPIOC, GPIO_Pin_13);delay_ms(500000);  // 延时}	}

点亮流水灯

#include "stm32f10x.h"                  // Device headervoid delay_ms(int miSec)
{while(miSec--);
}int main()
{//目标:点亮5个灯,并让5个灯能依次点亮,按照PA0 PA1 PA2 PA3 PA4 PA3 PA2 PA1 PA0这样的顺序循环//1.开GPIOA的时钟-GPIOA挂在APB2总线上的,(开APB2上面GPIOA的时钟)--APB2 ENABLE RegisterRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//2. 配置这个设备(PA0~PA4这个引脚),配置成推挽输出(00),配置的输出速度位50MHz,GPIO_InitTypeDef GPIO_InitStruct;//定义一个GPIO的结构体GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//模式设置成推挽输出GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;//引脚是0~4GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//电平切换速度50MHzGPIO_Init(GPIOA, &GPIO_InitStruct);//使用结构体配置PA0~PA4引脚//3. 使用这个设备(PA0~PA4引脚)while(1){// 正向循环:PA0→PA1→PA2→PA3→PA4for(int i = 0;i < 5;i++){//GPIO_SetBits(GPIOA, 1 << i);    // 点亮当前LEDGPIO_WriteBit(GPIOA, (GPIO_Pin_0 << i), 1);   // 点亮当前LED//或者pow(2, i)delay_ms(5000000);                  // 延时500ms//GPIO_ResetBits(GPIOA, 1 << i);  // 熄灭当前LEDGPIO_WriteBit(GPIOA, (GPIO_Pin_0 << i), 0); // 熄灭当前LED		//delay_ms(5000000); }// 反向循环:PA3→PA2→PA1→PA0for(int i = 4;i >= 0;i--){GPIO_SetBits(GPIOA, 1 << i);    // 点亮当前LED//GPIO_WriteBit(GPIOA, (GPIO_Pin_0 << i), 1);   // 点亮当前LEDdelay_ms(5000000);                  // 延时500msGPIO_ResetBits(GPIOA, 1 << i);  // 熄灭当前LED//GPIO_WriteBit(GPIOA, (GPIO_Pin_0 << i), 0); // 熄灭当前LED		//delay_ms(5000000); }
//		for (int i = 0; i < 8; i++) 
//		{
//			int pin;
//      if (i < 5) 
//			{
//				pin = i;        // 正向:0→1→2→3→4
//      } 
//			else 
//			{
//        pin = 8 - i;    // 反向:3→2→1→0
//      }
//		}
//      GPIO_SetBits(GPIOA, 1 << pin);
//      delay_ms(5000000);
//      GPIO_ResetBits(GPIOA, 1 << pin);	
}
题目 1:

已知变量a的值为十六进制0x5A,请使用按位与操作&,将其与十六进制0x0F进行按位与运算,计算并输出结果。

要求:

  • 请编写代码,使用&运算符,并输出结果。
int main()
{int a = 0x5A;a = a & 0x0F;//0101 1010//0000 1111//0000 1010printf("%d", a);//10
}
题目 2:

给定变量b的值为十六进制0x3C,使用按位或操作符|将其与十六进制0x07进行按位或运算,计算并输出结果。

要求:

  • 编写代码,使用|运算符,输出运算结果。
int main()
{int a = 0x3C;a = a | 0x07;//0011 1100//0000 0111//0011 1111  printf("%d", a);//63
}
题目 3:

已知变量c的值为十六进制0xFF,使用按位取反操作符~,对其进行按位取反操作,输出结果。

要求:

  • 请使用~运算符,编写代码并输出取反后的结果。
int main()
{int c = 0xFF; // 取反操作// 原始值:00000000 00000000 00000000 11111111// 取反后:11111111 11111111 11111111 00000000,这是补码//除符号位取反得到反码:10000000 00000000 00000000 11111111//加一得到原码:10000000 00000000 00000001 00000000c = ~c;printf("按位取反后的十进制结果:%d\n", c);     // -256printf("按位取反后的十六进制结果:0x%X\n", c); // 0xFFFFFF00(计算机中有符号整数通常以补码形式显示)  return 0;
}
题目 4:

将变量d的值左移2位。假设d = 0x15,输出左移后的值。

要求:

  • 使用左移操作符<<,编写代码并输出移位后的结果。
int main()
{int d = 0x15;  // 十六进制 0x15 = 十进制 21// 二进制为:0001 0101// 左移 2 位(每左移一位,相当于乘以 2)// 原始二进制:0001 0101// 左移2位后  0101 0100(低2位补0)// 十六进制结果为:0x54// 十进制结果为:84d = d << 2;printf("左移2位后的十进制结果:%d\n", d);     // 输出:84printf("左移2位后的十六进制结果:0x%X\n", d); // 输出:0x54return 0;
}
题目 5:

将变量e的值右移3位,假设e = 0x98,输出右移后的值。

要求:

  • 使用右移操作符>>,编写代码并输出移位后的结果。
#include <stdio.h>int main()
{int e = 0x98;  // 十六进制 0x98 = 十进制 152// 二进制为:1001 1000// 右移 3 位(每右移一位,相当于除以 2,整数部分保留)// 原始二进制:1001 1000// 右移3位后: 0001 0011(高位补0,算术右移)// 十六进制结果为:0x13// 十进制结果为:19//算术移位:右边丢弃,左边补原符号位。//逻辑移位:右边丢弃,左边补0。//大多数主流编译器在对 int 做右移时是算术右移,对 unsigned int 则是逻辑右移e = e >> 3;printf("右移3位后的十进制结果:%d\n", e);     // 输出:19printf("右移3位后的十六进制结果:0x%X\n", e); // 输出:0x13return 0;
}
题目 6:

给定一个8位的无符号整数f = 0b11001010,请计算以下操作:

  1. f的最低有效位清零(即按位与0b11111110)。
  2. f的第三位(从右数)置为1(即按位或0b00000100)。

要求:

  • 编写代码,使用&|进行位操作,输出每步操作后的结果。
int main()
{unsigned char f = 0b11001010; // 原始值 f = 0b11001010(即十进制 202)//0b 是 二进制字面量 的前缀// 用二进制表示每一位(f >> i) & 1for (int i = 7; i >= 0; i--) {printf("%d", (f >> i) & 1);}printf("\n");// 1. 将 f 的最低有效位清零(按位与 0b11111110)f = f & 0b11111110;  // 清零最低有效位// 2. 将 f 的第三位(从右数)置为 1(按位或 0b00000100)f = f | 0b00000100;  // 将第三位(从右数)置为 1return 0;
}
题目 7:

在给定的整数g = 0x2A中,清除其第4位(从右数,按位与0b11101111),并在第5位设置1(按位或0b00100000),输出最后的结果。

要求:

  • 请使用&|,编写代码并输出最后的结果。
#include <stdio.h>int main()
{unsigned char g = 0x2A; // 十六进制 0x2A = 二进制 00101010 = 十进制 42// 第一步:清除第4位(从右数),按位与 0b11101111(即清除 bit3)g = g & 0b11101111;// 第二步:设置第5位(从右数),按位或 0b00100000(即设置 bit5)g = g | 0b00100000;return 0;
}
题目 8:

int t=0x12345678,将t的47位,改为0x3,且1213位,改为0;

要求:

  • 请使用&|,编写代码并输出最后的结果。
int main()
{int t = 0x12345678;//0001 0010 0011 0100 0101 0110 0111 1000printf("改变前:%d\n", t);printf("改变前:0x%08x\n", 0x12345678);//把4~7位,改为0x3   0011//0001 0010 0011 0100 0101 0110 **0111** 1000//1. 先把t的第4~7位清空(变为0)/*先用0000 0000 0000 0000 0000 0000 0000 1111,即0xF,左移4位得0000 0000 0000 0000 0000 0000 1111 0000(即 0xF0)再取反后1111 1111 1111 1111 1111 1111 0000 1111(即 0xFFFFFF0F)最后再或上这个数,得到0001 0010 0011 0100 0101 0110 *0000* 1000*/t &= ~(0xF << 4);//2. 将0x3移位到4~7位/*0x3 的二进制是 0011左移 4 位,使其对齐到 4~7 位0x3 << 4 = 0000 0000 0000 0000 0000 0000 0011 0000最后再与上这个数,得到0001 0010 0011 0100 0101 0110 *0011* 1000*/t |= 0x3 << 4;//且12~13位,改为0//0001 0010 0011 0100 01*01* 0110 0011 1000//1. 直接把t的第12~13位清空(变为0)t &= ~(0x3 << 12);printf("改变后:%d\n", t);printf("改变后:0x%x\n", t); return 0;
}

位运算总结

一个数的某一位,置为0,只需要按位与一个需要置换的位置为0其他位置为1的数,因为&有0则0,一个数&1是本身,不变

一个数的某一位,置为1,只需要按位或一个需要置换的位置为1其他位置为0的数,因为|有1则1,一个数|0是本身,不变

一个数的某一位,进行反转,只需要异或一个需要置换的位置为1其他位置为0的数,因为相同(本身)为0相异为1,一个数0不变,1反转

要清零,用 &,掩码位是 0

要置 1,用 |,掩码位是 1

要翻转,用 ^,掩码位是 1

要取反,用 ~,整个数变反

Brian Kernighan 算法的核心是利用 n & (n - 1) 这个操作。这个操作能够把整数 n 的二进制表示里最右边的 1 置为 0

//--------------------编写一个代码求一个整数存储再内存中的二进制1的个数-------------------------
int countSetBits(int num) 
{int count = 0;// 循环直到 num 变为 0while (num){// 检查最低位是否为 1if (num & 1) {count++;}// 右移一位num >>= 1;// num = num >> 1}return count;
}//-------------------------------------方法二-----------------------int main() 
{int num = 15;int count = 0;while (num) {num = num & (num - 1);count++;}printf("%d\n", count);return 0;
}//---------------------修改二进制的某一位-----------------------------int a = 13;
00000000000000000000000000001101
现在要把倒数第二位改成1
需要 | 一个
00000000000000000000000000000010//1<<1,这个数可以理解为1向左移动了1位
即 a | (1 << 1)
a |= 1 << 1;
得到
00000000000000000000000000001111那么现在需要把a = 29
00000000000000000000000000011101
的第五位的1变成0
可以 & 一个
11111111111111111111111111101111
得到
00000000000000000000000000001101
那么11111111111111111111111111101111这个怎么得到
就可以由00000000000000000000000000010000~按位取反得到
即
a &= ~(1<<4);//a=a &~(1<<4)//-------------------不创建临时变量(第三个变量),实现两个整数的交换---------------int main()
{int a = 3;int b = 5;a = a + b;b = a - b;//总和-b得到aa = a - b;//总和-a得到breturn 0;
}但是这个方法会有潜在的问题,
a和b都是整形,都有最大值,
如果a和b都很大,那么a+b就超出了int能存储的最大值,溢出了。int main()
{int a = 3;int b = 5;a = a ^ b;b = a ^ b;a = a ^ b;return 0;
}3 ^ 3 = 0; --->a ^ a = 0;
011
011
000
两个相同的数(正负数都满足)异或为00 ^ 5 = 5; --->0 ^ a = a;
000
101
101
0和一个数(正负数都满足)异或为本身3 ^ 3 ^ 5 = 0 ^ 5 = 5;
3 ^ 5 ^ 3 = 5;
异或操作符支持交换律所以
int a = 3;
int b = 5;
a = a ^ b// a = 3 ^ 5
b = a ^ b;// b = 3 ^ 5 ^ 5 = 3
a = a ^ b;// a = 3 ^ 5 ^ 3 = 5思路:
想要a = b
先a = a ^ b;
b = a ^ b;
就得到了b = a;
现在是b = a; a = a ^ b;
想要a = b;
再a = a ^ b;
http://www.dtcms.com/a/278801.html

相关文章:

  • stm32-Modbus主机移植程序理解以及实战
  • argus/nvarguscamerasrc 远程显示报错
  • 项目一第一天
  • 纯数学专业VS应用数学专业:这两个哪个就业面更广?
  • C++后端面试八股文
  • Linux 基础命令详解:从入门到实践(1)
  • JAVA 并发 ThreadLocal
  • RestAssured(Java)使用详解
  • 19.数据增强技术
  • 管程! 解决互斥,同步问题的现代化手段(操作系统os)
  • Java行为型模式---模板方法模式
  • Imx6ull用网线与电脑连接
  • SpringBoot JAR 反编译替换文件
  • 【嵌入式汇编基础】-操作系统基础(三)
  • 【每日刷题】移动零
  • LabVIEW-Origin 船模数据处理系统
  • 【爬虫】Python实现爬取京东商品信息(超详细)
  • 期权和期货的区别主要是什么?
  • [论文阅读] 人工智能 | 用大型语言模型玩转多语言主观性检测:CheckThat! 2025赛事中的亮眼表现
  • Unity3D + VS2022连接雷电模拟器调试
  • 【PTA数据结构 | C语言版】字符串连接操作(不限长)
  • 分布式一致性协议
  • Android动画:属性动画以及实现点击图标缩放的动画效果
  • Relocations in generic ELF (EM: 40)
  • “国乙黑月光”指的是谁?
  • YOLOv11调参指南
  • Maven 依赖原则和依赖冲突
  • Docker入门指南(超详细)
  • Jetpack Compose 重组陷阱:一个“乌龙”带来的启示
  • yolo8+声纹识别(实时字幕)