STM32江科大教程实战指南:实践中的难点解析与程序实践(持续更新)

一、硬件连接类

1、仿真器配置

(1) 硬件接口配置

        这里采用的是普中的DAP 仿真器,感觉应该是年代久远了,几年前买的,资料缺失,缺失端口定义,在没找到资料前,实在是搞笑了!没错,接口搞错了。让右侧的几个输入端口(DIO、CLK)当成SWO的输出接口了。

正确的连接图(在某宝评论看到的)其实跟STlink的一致,记得load后要复位: 

下面是实物图,注意缺口方向:

(2) 软件配置

 调试方法类似:stlink调试

有所不同的是PZ在debug里选择的是:cmsis-DAP

BOOT可能其他方式也行,仅测试这一种接法:

2、boot  0、1 配置(已验证)

(1)flymcu串口烧录时1、0  

        烧录方法如下:

FLYMCU串口烧录STM32https://blog.csdn.net/weixin_42690036/article/details/129529745?ops_request_misc=&request_id=&biz_id=102&utm_term=flymcu%E7%83%A7%E5%BD%95stm32%E6%9C%80%E5%B0%8F%E7%89%88&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-129529745.142%5Ev100%5Epc_search_result_base2&spm=1018.2226.3001.4187        对应BOOT :

USB转TTL接口连接:

RXD—–A9

TXD—–A10

3V3—-3.3

GNG—G

(2) 连接仿真器在线调试时  00

 这里使用仿真器调试,用keil 软件  load程序后,需要按单片机最小板复位键使程序生效

hhhh江科大入门点灯程序已烧录完毕,灯已点亮

3、STM32最小系统板的引脚原理图(经常可用到)

        这里参考了CSDN大佬的图片:

 二、软件程序类

待编辑

1、引用头文件问题,未找到自定义封装的.h头文件

正常无错误的话,需要编译后生效;若有错:

这里涉及的问题可能比较多,如果只报一个错,

(1)请仔细检查.c  .h  或main.c中的分号是否缺失或为英文状态;

(2)程序结尾需空行;添加文件时的路径请确保正确。

(3).h文件结构体是否完整(头尾);

问题解决,无报错:

2、OLED显示/调试

        包含串口调试、显示屏、keil调试;采用0.96寸OLED模块,3-5.5v供电,I2C/SPI通信,128*64分辨率;

(1)硬件电路

        SCL—PB8     

        SDA—PB9

(2)驱动函数

        这里OLED.c文件中已包含了#include "OLED_Font.h"头文件,主函数中不需要再次声明,否则会报错!

        程序烧录后不亮屏幕的:

1、可以将OLED.c中的GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD替换为GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;推挽输出试一试;

2、OLED.C初始化部分添加GPIO_SetBits(GPIOB,GPIO_Pin_8 | GPIO_Pin_9);

3、VCC接5V接口,非3.3V;修改为其它引脚。

4、正负极千万别接反!

5、板子在面包板上未插紧。搞了半天GND接口问题,换了个GND接口就可以了,程序烧录后只显示一下就灭掉的。

 还有就是显示时不支持直接输入二进制,需要输入为16进制。

3、红外传感器计数

        (1)硬件部分

        注意传感器接线的GPIO口在IGPIOB输入端口。

        (2)软件程序

        这里出现了屏幕只亮一半的问题,原因:电压稳定问题‌:‌刚上电时电压不稳定,‌立即初始化OLED屏可能会出问题。‌解决方法是在初始化前加个延时,‌等待电压稳定‌。

        在sensor.c文件中,本文用的引脚为PB13与视频教程不同,最后要加上这一行:

EXTI_ClearITPendingBit(EXTI_Line13);        //清除外部中断13号线的中断标志位
                                                    //中断标志位必须清除
                                                    //否则中断将连续不断地触发,导致主程序卡死

4、定时器中断

(1)结构

三、涉及程序汇总

程序资料:程序资料下载

1、点灯:
#include "stm32f10x.h"                  // Device header
//#库函数点灯
int main(void)
{
//	#RCC->APB2ENR = 0X00000010;#
//	#GPIOC->CRH = 0X00300000;
//	#GPIOC->ODR = 0X00002000;
//	
//	#使能时钟------1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	#结构体    端口模式-----
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//这里是推挽输出,点灯用
	
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//端口+速率
	
//	GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	//高低电平
//	GPIO_SetBits(GPIOC, GPIO_Pin_13);
//	GPIO_ResetBits(GPIOC, GPIO_Pin_13);

	while(1)
	{
//		GPIO_Write(GPIOA, ~0x0001);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x0002);//0000 0000 0000 0001
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x0004);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x0008);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x00010);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x00020);
//		Delay_ms(500);
		
//		GPIO_ResetBits(GPIOA, GPIO_Pin_0);
//		Delay_ms(500);
//		
//		GPIO_SetBits(GPIOA, GPIO_Pin_0);
//		Delay_ms(500);
//		
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//亮
		Delay_ms(300);
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭
		Delay_ms(500);
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//亮
		Delay_ms(200);
		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭
		Delay_ms(100);
		
	}
}
2、流水灯/蜂鸣器(蜂鸣器类似、工位安静就不测试了)

参考1修改 while部分、初始化部分

#include "stm32f10x.h"                  // Device header
//#库函数点灯
int main(void)
{
//	#RCC->APB2ENR = 0X00000010;#
//	#GPIOC->CRH = 0X00300000;
//	#GPIOC->ODR = 0X00002000;
//	
//	#使能时钟------1
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	#结构体    端口模式-----
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//这里是推挽输出,点灯用
	
	
//	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
	
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//端口+速率
	
//	GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	
	//高低电平
//	GPIO_SetBits(GPIOC, GPIO_Pin_13);
//	GPIO_ResetBits(GPIOC, GPIO_Pin_13);

	while(1)
	{
//		GPIO_Write(GPIOA, ~0x0001);
//		Delay_ms(500);
		
				GPIO_Write(GPIOA, ~0x0001);//0000 0000 0000 0001对应PA0-PA15端口~低电平点亮
		Delay_ms(500);
				GPIO_Write(GPIOA, ~0x0002);//0000 0000 0000 0010对应PA0-PA15端口~低电平点亮
		Delay_ms(500);
				GPIO_Write(GPIOA, ~0x0004);//0000 0000 0000 0100对应PA0-PA15端口~低电平点亮
		Delay_ms(500);
				GPIO_Write(GPIOA, ~0x0008);//0000 0000 0000 1000对应PA0-PA15端口~低电平点亮
		Delay_ms(500);
				GPIO_Write(GPIOA, ~0x0010);//0000 0000 0001 0000对应PA0-PA15端口~低电平点亮
		Delay_ms(500);
				GPIO_Write(GPIOA, ~0x0020);//0000 0000 0010 0000对应PA0-PA15端口~低电平点亮
		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x0004);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x0008);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x00010);
//		Delay_ms(500);
//				GPIO_Write(GPIOA, ~0x00020);
//		Delay_ms(500);
		
//		GPIO_ResetBits(GPIOA, GPIO_Pin_0);
//		Delay_ms(500);
//		
//		GPIO_SetBits(GPIOA, GPIO_Pin_0);
//		Delay_ms(500);
//		
//		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//亮
//		Delay_ms(200);
//		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭
//		Delay_ms(100);
//		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//亮
//		Delay_ms(200);
//		GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//熄灭
//		Delay_ms(100);
		
	}
}


效果:

3、按键/温度/红外传感器控制   蜂鸣器与LED类似

        这里是将led、key部分进行了封装,封装文件在文章头部,此处仅提供主函数

#include "stm32f10x.h"                  // Device header
//#库函数点灯----------------这个是按键点灯key、led库文件在文章开头附件资源
#include "Delay.h"
#include "led1.h"
#include "key.h"

uint8_t KeyNum;

int main(void)
{
	LED_Init();
	Key_Init();
	


	while(1)
	{
		KeyNum=Key_GetNum();
		if (KeyNum == 1)
		{
			LED1_Turn();
		
		}
		if (KeyNum == 2)
		{ 
			LED2_Turn();
		
		}
//		LED1_ON();
//		LED2_OFF();
//		Delay_ms(1000);
//		LED1_OFF();
//		LED2_ON();
//		Delay_ms(1000);
		
	}
	
}



#include "stm32f10x.h"                  // Device header
//#库函数这个是光敏传感器的
#include "Delay.h"
#include "led1.h"
#include "beep.h"
#include "red.h"

uint8_t KeyNum;

int main(void)
{
	BUZZER_Init();
	
	LED_Init();
	
	RED_Init();

	while(1)
	{
		if (RED_Get()==1)
		{
		LED1_ON();
		
		}
		else
			
			LED1_OFF();
	
		
	}
	
}




//red.c文件与key类似

#include "stm32f10x.h"                  // Device header
#include "Delay.h"


void RED_Init(void)
{

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	
	GPIO_InitTypeDef GPIO_INITINSTRUCT;
	GPIO_INITINSTRUCT.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_INITINSTRUCT.GPIO_Pin=GPIO_Pin_13;
	GPIO_INITINSTRUCT.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_INITINSTRUCT);


}

uint8_t RED_Get(void)    //返回码
{
	
	
return GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_13);/读取
	
}
    
 



4、OLED显示
#include "stm32f10x.h"                  // Device header
//#库函数点灯
#include "Delay.h"
#include "led1.h"
#include "OLED.h"
#include "red.h"
//#include "OLED_Font.h"



int main(void)
{
//	Delay_ms(500);
	OLED_Init();
	 
	OLED_ShowChar(1,1,'A');            //行+列+字符
	OLED_ShowString(1,3,"HELLO CSDN");//可用于覆盖删除
	OLED_ShowNum(2,1,12345, 5);
	OLED_ShowBinNum(4,1,0xAA55,16);
	OLED_ShowHexNum(3,1,0xAA55, 4);//16进制
	OLED_ShowSignedNum(2,7, -66, 2);
	
	

	while(1)
	{
		

	}
}

显示效果(相机原因最后一行没拍上):

5、 EXTI外部中断/红外传感器计数/编码器计数

        这里STM32的中断有EXTI、TIM、ADC、USART、SPI、I2C

#include "stm32f10x.h"                  // Device header
//#库函数点灯
#include "Delay.h"
#include "led1.h"
#include "OLED.h"
#include "sensor.h"

//#include "OLED_Font.h"



int main(void)
{
	
	Delay_ms(500);//延时函数很有必要
//	Delay_ms(500);
	OLED_Init();
	
	sensor_Init();
	
	
	 
//	OLED_ShowChar(1,1,'A');            //行+列+字符
	OLED_ShowString(1,2,"LOVE & FREEDOM");//可用于覆盖删除
	OLED_ShowString(4,2,"H   U    A   T");
	OLED_ShowNum(3,1,1111111111, 10);
	OLED_ShowString(2,1,"Count:");
//	OLED_ShowHexNum(3,1,0xAA55, 4);//16进制
//	OLED_ShowSignedNum(2,7, -66, 2);
	
	while(1)
	{
		OLED_ShowNum(2, 7, sensor_Get(), 5);

	}
}

--------------------------------------------------
库文件
#include "stm32f10x.h"                  // Device header

uint16_t sensor_Count;



void sensor_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource13);//中断
	EXTI_InitTypeDef EXTI_InitStructure;
	EXTI_InitStructure.EXTI_Line=EXTI_Line13;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Rising;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//分组,优先级,仅需一次
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn;//通道
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//优先级
	
	NVIC_Init(&NVIC_InitStructure);//结构体地址
	
	
}


uint16_t sensor_Get(void)
{

	return sensor_Count;

}


void EXTI15_10_IRQHandler(void)
{
	if (EXTI_GetITStatus(EXTI_Line13) == SET)		//判断是否是外部中断14号线触发的中断
	{
		/*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
		if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0)
		{
			sensor_Count ++;					//计数值自增一次
		}
		EXTI_ClearITPendingBit(EXTI_Line13);		//清除外部中断14号线的中断标志位
													//中断标志位必须清除
													//否则中断将连续不断地触发,导致主程序卡死
	}
}

/

#include "stm32f10x.h"                  // Device header
//#库函数点灯
#include "Delay.h"
#include "led1.h"
#include "OLED.h"
#include "sensor.h"
#include "encode.h"
//#include "OLED_Font.h"

int16_t Num;			//定义待被旋转编码器调节的变量

int main(void)
{
	
	Delay_ms(600);
//	Delay_ms(500);
	OLED_Init();
	Encoder_Init();
	sensor_Init();
	
//	LED_Init();
//		LED1_ON();
//		Delay_ms(500);
//		LED1_OFF();
//		Delay_ms(500);
//		LED2_ON();
//		Delay_ms(500);
//		LED2_OFF();
//		Delay_ms(500);

	 
//	OLED_ShowChar(1,1,'A');            //行+列+字符
	OLED_ShowString(1,2,"LOVE & FREEDOM");//可用于覆盖删除
		OLED_ShowString(2,3,"Count:");
//		OLED_ShowNum(3,5,202211068, 9);
//	OLED_ShowString(4,2,"H   U    A   T");


//	OLED_ShowHexNum(3,1,0xAA55, 4);//16进制
//	OLED_ShowSignedNum(2,7, -66, 2);
	
	while(1)
	{
		OLED_ShowNum(2, 9, sensor_Get(), 7);
		
		OLED_ShowString(4, 2,"H   U    A   T");
		Delay_ms(2000);
		OLED_ShowString(4, 2,"N    O   .   1");
		Delay_ms(2000);
		Num += Encoder_Get();				//获取自上此调用此函数后,旋转编码器的增量值,并将增量值加到Num上
		OLED_ShowSignedNum(3, 5, Num, 5);	//显示Num

//		LED1_Turn();	
//		Delay_ms(500);
//		LED2_Turn();
//		Delay_ms(500);
//		LED2_OFF();
//		LED2_OFF();

	}
}


现象:

6、定时器中断/PWM呼吸灯

定时器计数用,

main
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"

uint8_t i;			//定义for循环的变量

int main(void)
{
	/*模块初始化*/
	OLED_Init();		//OLED初始化
	PWM_Init();			//PWM初始化
	
	while (1)
	{
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(i);			//依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
			Delay_ms(10);				//延时10ms
		}
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(100 - i);	//依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
			Delay_ms(10);				//延时10ms
		}
	}
}

pwm.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO重映射*/
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;     //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
	
	/*输出比较初始化*/
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;		//输出极性,选择为高,若选择极性为低,则输出高低电平取反
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;	//输出使能
	TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);						//将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

呼吸灯现象:

作者:电气一游民

物联沃分享整理
物联沃-IOTWORD物联网 » STM32江科大教程实战指南:实践中的难点解析与程序实践(持续更新)

发表回复