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的值
}
呼吸灯现象:
作者:电气一游民