一、安装及基本技能
1.环境安装
MDK的下载与安装:
下载链接:Arm Keil | Keil STM32G0xx_DFP
【STM32】两个版本MDK搭建和三种调试器的使用-CSDN博客
安装完MDK后要导入MDK Keil中
导入下载的MDK**【记得保存路径中不要有中文】**
安装DFP包提示SVDConv exied with an error.No uVision SystemViewer file_svdconv exited with an error-CSDN博客
FW的下载与安装
【STM32】STM32的Cube和HAL生态-CSDN博客
ST_Link驱动安装
2.烧录空代码--STLink的使用
1.先通过CubeMX创建一个空文件
2.配置STLink
【STM32】两个版本MDK搭建和三种调试器的使用-CSDN博客
此时并没有接收到,表示未上电
上电后发现还是没有识别,则再一次关闭,重新打开即可
注意点:要对应开发板上的设置
3.基本技能--点亮LED
1.原理图分析
LED1:一旦通电就会亮
LED2:一端3.3V,另外一端就要接通低电平才会亮【设置为输出引脚,因为我是直接将其赋值】
2.相关步骤
1)将LED0变为低电平才会点亮(模拟接地)
2)找到芯片对应的引脚(PB7),给他赋高电平或者低电平来改变蓝灯的亮灭
3.使用CubeMX生成相关代码
1.设置GPIO
由上图分析可知将PB7设置为输出引脚
2.设置RCC
4.基本技能--按键控制LED
1.实验目标
点亮STM32板子上的LED灯,并使用按键控制灯的亮灭
2.实验用到知识点
按键原理图
按键消抖
HAL库中的HAL_GPIO_TogglePin和HAL_ReadPin函数
3.分析原理图
分析可知当按键未按下时,3.3V沿着PC13输出为高电平,所以接通时PC13会被拉低
因为默认在3.3V后面加上上拉电阻,所以我们不用额外添加上拉电阻,所以使用输入浮空就可以
4.使用CubeMX生成相关代码
1.设置GPIO
LED还是按照上面的设置:output
KEY在新设置:input【因为是从3.3V流过来的,所以其实我们是检测PC13处的电平】
2.设置RCC
5.代码编写
1.led闪烁
cpp
while (1)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
HAL_Delay(1000);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
HAL_Delay(1000);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
2.检测按键:HAL_GPIO_WritePin
按下按键为亮,松开为灭
cpp
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
//读取按键状态
HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//如果读取到按键为低电平,说明按下按键
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
//如果按下,将led点亮
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
}
else{//反之按下没有反应
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
3.检测按键:HAL_GPIO_TogglePin
每一次按下按键都会转换电平状态
cpp
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
//如果按下,将led点亮
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);
}
4.按键消抖
cpp
//按键消抖
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
//如果按下,将led点亮
HAL_Delay(100);//按键消抖
if(HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_13)==GPIO_PIN_RESET){
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_7);//反转GPIO电平
}
}
6. 步骤
1)查看按键原理图
2)判断对应的GPIO口的电平状态
3)判断GPIO是输入端还是输出端
4)按键需要消抖
二、电机模块
0.直流电机原理
受力不平衡
1.小车前进
1.知识点
直流电机的驱动
STM32引脚电平状态
2.小车底板原理图
3.电机使能
发现我们对应的是H2插座的1和2
分析可以知道对应PB5和PB4
4.电机转动方向
IN-和IN+一定不能同时为1或者为0
一个为0则另外一个为1
5.代码编写
cpp
//电机使能
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4 |GPIO_PIN_5, GPIO_PIN_SET);
while (1)
{
//电机前进:则将左右的IN+设置为高电平,左右的IN-设置为低电平
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3 |GPIO_PIN_11, GPIO_PIN_SET);//IN+
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6 |GPIO_PIN_12, GPIO_PIN_RESET);//IN-
}
6.步骤
1)电机驱动是依靠什么驱动(电流流过此次产生力)
2)找到小车底板上对应GPIO引脚
3)开启左轮和右轮的使能端
4)使两轮对应的IN+设为高电平,IN-设置为对应的低电平
2.小车后退
步骤与小车前进部分差不多
1.函数封装
cpp
void backword(){
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
void forword(){
//电机前进:则将左右的IN+设置为高电平,左右的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
2.按键设置
cpp
//按键检测
int key_detection(){
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==GPIO_PIN_RESET){
return 1;
}
return 0;
}
3.goto语句
cpp
while (1)
{
A:while(1){
forword();
if(key_detection()==1){
HAL_Delay(100);//消抖
if(key_detection()==1){
goto B;
}
}
}
B:while(1){
forword();
if(key_detection()==1){
HAL_Delay(100);//消抖
if(key_detection()==1){
goto A;
}
}
}
}
4.完整代码
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
void backword(){
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
void forword(){
//电机前进:则将左右的IN+设置为高电平,左右的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//按键检测
int key_detection(){
if(HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin)==GPIO_PIN_RESET){
return 1;
}
return 0;
}
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
//使能电机
HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin|MOTOR_R_S_Pin,GPIO_PIN_SET);
//定义按键
HAL_GPIO_ReadPin(KEY_GPIO_Port,KEY_Pin);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
A:while(1){
forword();
if(key_detection()==1){
HAL_Delay(100);//消抖
if(key_detection()==1){
goto B;
}
}
}
B:while(1){
forword();
if(key_detection()==1){
HAL_Delay(100);//消抖
if(key_detection()==1){
goto A;
}
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 16;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
3.小车完整代码
1.原理图
2.代码编写
cpp
void backword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
void forword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}
//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
//使能电机
HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
三、循迹模块:LM339
1.基本原理
1.原理图
LED3是input引脚
2.红外循迹原理
- 红外对管中,其中的白色为发射红外信号的,黑色为接收管,未接收到信号时不会导通(LED1不会亮),接收到红外信号会导通(LED1会亮)。
- 接收管的导通内阻大小也与接收到多少有效信号有关,接收的有效信号越多,导通的内阻越小,下图的LED也就会越亮。
- 如下图,当红外发射管发出去的信号没有被反弹回来时,红外接收管不会被导通,此时LED1就不会亮,当发射管的信号被反弹回来并被接收管收到后,红外接收管被导通,此时LED1亮起。
3.红外灵敏度
1)由红外接收器和红外发收器离地面的高度有关
2)与负极的电阻大小有关
4.LM339比较器
1)当我们循迹的时候,小车底下的黑色线不会被压倒,则红外发射器发射的红外线可以被红外接收器接收到,则对应的红外模块的小灯不会亮。(因为选择器中的正电压>负电压,所以此时小灯两端都为正电压)
2)在红外循迹模块中红外接收是光线越大,阻值越小
3)
4)判断是否压线--->判断引脚是否为0
2.左边越界后用户led亮起
1.原理图分析
左边越界,表示左边的红外板块上的灯会亮起
2.CubeMX设置
3.代码编写
当小车越界的时候,用户led会亮,红外板块上的灯亮【不正常工作】
当小车没有越界的时候,用户led不会亮【正常工作】
cpp
while (1)
{
//如果红外模块上的led亮起,说明未接通,表示不能接收到红外线
//如果红外模块上的led不亮,说明接收到红外线
if(HAL_GPIO_ReadPin(tracking_left_GPIO_Port,tracking_left_Pin)==0){//此时引脚上为低电平,则此时红外板块的灯亮
//表示输出为高电平,说明红外接收器接收到信号,则用户led亮,红外板块led亮起
HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_RESET);
}else{//表示接收到红外信号【正常工作】,则用户led不亮,红外板块led不会亮
HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_SET);
}
}
3.右边越界后led闪烁
分析过程与上面类似于
1.原理图
2.代码编写
cpp
//led闪烁
void led_start(){
// HAL_GPIO_WritePin(led_GPIO_Port,led_Pin,GPIO_PIN_SET);
// HAL_Delay(1000);
// HAL_GPIO_WritePin(led_GPIO_Port,led_Pin,GPIO_PIN_RESET);
HAL_GPIO_TogglePin(led_GPIO_Port,led_Pin);
HAL_Delay(1000);
}
int main(void)
{
while (1)
{
//如果led亮说明没有接收到红外线,如果led不亮说明接收到红外线
if(HAL_GPIO_ReadPin(tracking_right_GPIO_Port,tracking_right_Pin)==1){
//此时检测为高电平,说明与红外连接的led不会亮,说明红外接收不到红外线
//此时将led的指示灯设置为闪烁
led_start();
led_start();
}else{
//说明红外接收器正常的接收到了红外,此时指示灯led不会亮
HAL_GPIO_WritePin(led_GPIO_Port,led_Pin,GPIO_PIN_SET);
}
}
}
4.车头越界后led改变状态
1.实验现象
1)如果左边越界,则用户指示灯闪烁减慢【HAL_Delay(1000)】,红外模块上的led熄灭
2)如果右边越界,则用户指示灯闪烁一般速度【HAL_Delay(500)】,红外模块上的led熄灭
3)如果正常行驶,用户指示灯熄灭【HAL_GPIO_WritePin(SET)-->输入高电平,熄灭】,红外模块上的led都熄灭
4)如果左右都越界(整个车头越界),则用户指示灯闪烁加快【HAL_Delay(50)】
2.代码编写
cpp
/**
1)如果左边越界,则用户指示灯闪烁减慢【HAL_Delay(1000)】,红外模块上的led熄灭
2)如果右边越界,则用户指示灯闪烁一般速度【HAL_Delay(500)】,红外模块上的led熄灭
3)如果正常行驶,用户指示灯熄灭【HAL_GPIO_WritePin(SET)-->输入高电平,熄灭】,红外模块上的led都熄灭
4)如果左右都越界(整个车头越界),则用户指示灯闪烁加快【HAL_Delay(50)】
*/
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//越界判断
int cross(){
//【注意点】
//判断整个车头是否越界应该放在第一个进行判断,因为如果先将单个判断放在第一个则后面无法进行判断
if(HAL_GPIO_ReadPin(GPIOB,IR_TRACK_L_Pin) && HAL_GPIO_ReadPin(GPIOB,IR_TRACK_R_Pin)==0){//表示两边都越界【整个车头】
return 1;
}else if(HAL_GPIO_ReadPin(GPIOB,IR_TRACK_L_Pin)==0){//表示左边越界
return 2;
}else if(HAL_GPIO_ReadPin(GPIOB,IR_TRACK_R_Pin)==0){//表示右边越界
return 3;
}else{
return 4;
}
}
//led闪烁速度转换
void led_speed(unsigned int time){
HAL_GPIO_TogglePin(user_led_GPIO_Port,user_led_Pin);
HAL_Delay(time);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
switch(cross()){
//整个车头越界
case 1:
led_speed(50);
break;
//左边越界
case 2:
led_speed(1000);
break;
//右边越界
case 3:
led_speed(500);
break;
//正常行驶时,用户指示灯熄灭
case 4:
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 16;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
5.左边越界后小车右转
1.设置电机模块
2.代码编写
cpp
//前进
void forward(){
//使能左右电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port,MOTOR_R_S_Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port,MOTOR_L_S_Pin,GPIO_PIN_SET);
//IN+:设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);
//IN-:设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);
}
//右转
void go_right(){
//失能右电机(right)
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port,MOTOR_R_S_Pin,GPIO_PIN_RESET);
//使能左电机(left)
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port,MOTOR_L_S_Pin,GPIO_PIN_SET);
//IN+:设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);
//IN-:设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);
}
//左转
void go_left(){
//使能右电机(right)
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port,MOTOR_R_S_Pin,GPIO_PIN_SET);
//失能左电机(left)
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port,MOTOR_L_S_Pin,GPIO_PIN_RESET);
//IN+:设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);
//IN-:设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(HAL_GPIO_ReadPin(IR_TRACK_L_GPIO_Port,IR_TRACK_L_Pin)==0){
//表示左边此时越界,则应该往右走
go_right();
}else if(HAL_GPIO_ReadPin(IR_TRACK_R_GPIO_Port,IR_TRACK_R_Pin)==0){
//表示右边此时越界,则应该往左走
go_left();
}
else{//说明此时为不越界
forward();
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
6.完整代码
当整个车头越界时,会后退,然后再重新规划路线
cpp
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void backword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
void forword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}
//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
//使能电机
HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//这里还有问题:待解决~~~~~~~~~~~~【但是前后退模块是正确的】
if(HAL_GPIO_ReadPin(IR_TRACK_L_GPIO_Port,IR_TRACK_L_Pin) && HAL_GPIO_ReadPin(IR_TRACK_R_GPIO_Port,IR_TRACK_R_Pin)==0){
//说明此时整个车头越界
//先让其后退
backword();
//在让其延时一会(后退一会)
HAL_Delay(500);
//在让其左/右转,在循环多次之后,肯定会找到一个合适的路线继续行驶
right_forward();
}else if(HAL_GPIO_ReadPin(IR_TRACK_L_GPIO_Port,IR_TRACK_L_Pin)==0){
//说明此时左边越界,应该往右边走
right_forward();
}else if(HAL_GPIO_ReadPin(IR_TRACK_R_GPIO_Port,IR_TRACK_R_Pin)==0){
//说明此时右边越界,应该往左边走
left_forward();
}else{
//都没有越界,则往前走
forword();
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
四、避障模块:LM324
0.红外避障基本原理
1.红外避障实现方案
- 红外避障实现采用红外对管实现,红外对管是一个总称,包含红外发射管和红外接收管,有不同的封装形式和内部结构稍有不同,但原理都一样。
- 我们的小车红外发射管和红外接收管采用的封装形式为5mm圆头直插封装。
2.红外对管原理
- 红外对管中,其中的白色为发射红外信号的,黑色为接收管,未接收到信号时不会导通,接收到红外信号会导通。
- 接收管的导通内阻大小也与接收到多少有效信号有关,接收的有效信号越多,导通的内阻越小,下图的LED也就会越亮。(当出现障碍物时,接收到的光变多,阻值变大,正电压变小,此时正电压<负电压。则输出负电压,则led1亮)-->出现障碍物时,led1会亮【则判断是否遇到障碍物,就对引脚进行判断,如果引脚==0,表示遇到,如果不为0,则表示没有遇到】
- 如下图,当红外发射管发出去的信号没有被反弹回来时,红外接收管不会被导通,此时LED1就不会亮,当发射管的信号被反弹回来并被接收管收到后,红外接收管被导通,此时LED1亮起。
U4的位置你可以理解为存在一个光敏电阻,电阻随着光照强度增大而增大,所以这个LM339+的位置是不是电流就小了,而我们的LM339-是一个滑动变阻器这是一个可调节的,当成为一个默认值的时候是不是只需要看+的电压大小,那光照强度高,电阻大,是不是+电压就小了,那进行比较的时候比较器会输出一个逻辑0,所以LED1就亮了,而离障碍物远的时候,因为光照强度弱了电阻小了,5的电压大,+大于-,所以led不亮
1.左边有障碍物用户led亮起
当左边遇到障碍物时,板上的led亮(因为障碍物会反射,如果物体离得越近,表示接收到的光越多),我们设置用户led灯亮
注意点:避障引脚为input(PA11和PA12)--->因为我们是要读取该引脚的电平【如果是设置引脚电平则为output】
1.原理图
分析可得:如果要判断是否有障碍物,则判断引脚上是否为0(因为障碍物反射,则U4光照强度增加,阻值增大,电压减少,正电压<负电压,则输出0),如果为0则表示遇到障碍物,红外板子灯亮起
引脚为0---》有障碍物---》亮
引脚为1--》无障碍物---》不亮
2.CubeMX使用
注意点:避障引脚为input(PA11和PA12)--->因为我们是要读取该引脚的电平【如果是设置引脚电平则为output】
3.代码编写
cpp
while (1)
{
if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0){//此时表示遇到障碍物,led1亮
//此时我们点亮用户led
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
}else{//此时表示遇到障碍物,led1不亮
//此时我们不点亮用户led
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
2.右边有障碍物用户led亮起
原理同上面一致
1.代码编写
cpp
while (1)
{
if(HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin==0){//此时表示遇到障碍物,led1亮
//此时我们点亮用户led
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
}else{//此时表示遇到障碍物,led1不亮
//此时我们不点亮用户led
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
3..前方有障碍物用户led亮起
前面两个同时越界--->led闪烁
左边有障碍物--->led会亮,设置用户led也亮
右边有障碍物----->led会亮,设置用户led也亮
都无障碍物-->两个led都灭
cpp
while (1)
{
if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin) && HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0){
//表示前面两个都遇到障碍物
led_speed();
}
else if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0){//此时表示遇到障碍物,led1亮
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
}else if(HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0){
//遇到障碍物用户led亮起来
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
}else{//此时表示没有遇到障碍物,led1不亮
//此时我们不点亮用户led
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
4.前方有障碍物代码
cpp
void backword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
void forword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}
//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
//使能电机
HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if((HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0)&& (HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0)){
backword();
HAL_Delay(500);
right_forward();
}
else if(HAL_GPIO_ReadPin(IR_OA_L_GPIO_Port,IR_OA_L_Pin)==0){
//此时表示左边遇到障碍物,则led1亮起
//我们应该想右转
right_forward();
}else if(HAL_GPIO_ReadPin(IR_OA_R_GPIO_Port,IR_OA_R_Pin)==0){
//此时表示右边遇到障碍物,则led1亮起
//我们应该想左转
left_forward();
}else{
//没有遇到障碍物向前
forword();
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
五、红外遥控模块
1.红外收发的应用场景
2.红外遥控的编码协议(NEC PROTOCOL脉冲宽度调制)
第一步:查看脉冲时间
接收端:区别在于脉冲时间的长短
脉冲时间是一个范围
实际上数据有33位=1位同步码头+32位数据位
第二步:输入捕获功能(识别PWM)
脉冲的时间=value1和value2之间的差值
就要使用到上升沿和下降沿的读取【主要看tim.c文件中的函数】
第三步:硬件电路引脚(红外接收&&串口)
因为我们是要通过判断引脚上的值来进行分析,所以应该使用输入引脚,输入捕获(查看PWM) 最为合适--->定时器模块
3.CubeMX生成
1.TIME-->输入捕获(PWM)
2.USART--串口输出
3.时钟设置
4.代码编写
1.编写定时器中断处理函数
cpp
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){
}
2.判断是上升沿还是下降沿
1.HAL_TIM_ReadCapturedValue: 从Capture Compare单元读取捕获值
2.__HAL_TIM_SET_CAPTUREPOLARITY:触发捕获事件的信号边沿
在运行时设置 TIM Capture x 输入极性。【设置捕获模式(上升沿,下降沿)
3.代码
cpp
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){
//判断是否为上升沿捕获
if(isUpcat){
//将捕获的定时器的值赋值给valueUp
valueUp=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
//将上升沿置为0
isUpcat=0;//是否为上升沿的标志位
//上升沿捕获数据
__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
}else{//捕获下降沿
//将捕获的定时器的值赋值给valueDown
valueDown=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
//将上升沿置为1
isUpcat=1;//是否为上升沿的标志位
//下升沿捕获数据
__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
}
}
此时一个脉冲宽度已经设置好了
脉冲宽度=下降沿捕获-上升沿捕获
3.脉冲数据填充
1)判断是否在脉冲频率范围内【判断接收码头是否正确】
2)判断buffer中是否已经满
3)当填充完数据,记得将指针设置为初始状态
cpp
//上升沿
uint32_t isUpcat=1;//表示默认为上升沿
uint32_t valueUp=0;
uint32_t valueDown=0;
//脉冲宽度
uint32_t kuandu=0;
uint16_t buffer[128]={0};
uint16_t buffer_ID=0;
//接收标志位:判断此时buffer中的数据是否已经填满
uint16_t rvcFlag=0;
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(){
//判断是否为上升沿捕获
if(isUpcat){
//将捕获的定时器的值赋值给valueUp
valueUp=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
//将上升沿置为0
isUpcat=0;//是否为上升沿的标志位
//触发捕获事件的信号边沿
__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
}else{//捕获下降沿
//将捕获的定时器的值赋值给valueDown
valueDown=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
//将上升沿置为1
isUpcat=1;//是否为上升沿的标志位
//触发捕获事件的信号边沿
__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
//计算差值
kuandu=valueDown-valueUp;//取到了脉冲宽度
//判断输入进来的数据是否是我们想要的
if(kuandu>=4400 && kuandu <=4600){//接收码头的范围
buffer_ID=0;//这个值用来判断我遍历到数组中的哪一个位置
buffer[buffer_ID++]=kuandu;//将我们获得的数据填入
}else if(buffer_ID>0){//表示此时我们还没有填充完整
//接着往下填充
buffer[buffer_ID++]=kuandu;
if(buffer_ID>32){//表示接收完毕
rvcFlag=1;//红外接收标志位
//将指针设置回开头
buffer_ID=0;
}
}
}
}
4.串口测试代码
cpp
/* USER CODE BEGIN 2 */
//发生测试串口代码
HAL_UART_Transmit(&huart1,(uint8_t)*ceshi,strlen(ceshi),100);
5.使能中断
开启定时器的IT中断
开启输入捕获(IC_IT)
cpp
//使能输入捕获中断【定时器中的】
HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_1);
//使能定时器中断
HAL_TIM_Base_Start_IT(&htim1);
6.获取数据并打印
cpp
while (1)
{
if(rvcFlag){
for(int i=0;i<33;i++){
//将从红外中获取的数据打印给printfbuffer
sprintf(printfbuffer," %u",buffer[i]);
//显示测试代码
//HAL_MAX_DELAY:最大超时时间
HAL_UART_Transmit(&huart1,(uint8_t*)printfbuffer,strlen(printfbuffer),HAL_MAX_DELAY);
//换行
if(i%8==0){
HAL_UART_Transmit(&huart1,"\n",2,HAL_MAX_DELAY);
}
}
//将标志位清零
rvcFlag=0;
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
点击按键0
7.将获取到的数值转换为逻辑1或者逻辑0
修改为逻辑表示,方便我们编写代码
我们在设置时将范围给大一点
cpp
//将获取到的遥控数值转换为逻辑1或者逻辑0
for(int i=0;i<8;i++){
if(buffer[16+i]>=1400 && buffer[16+i]<1700){//这里16+i表示第17位开始才为控制码
yaokong |=0x01;//将其设置为逻辑1
}else if(buffer[16+i]>=400 && buffer[16+i]<=600){
yaokong |=0x00;//将其设置为逻辑0
}else {
//错误处理
}
yaokong<<=1;//存放数值
}
注意点:
yaokong要左移动1位
8设置对应的按键控制值
利用串口助手进行设置
5.完整代码
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
//上升沿
uint32_t isUpcat=1;//表示默认为上升沿
uint32_t valueUp=0;
uint32_t valueDown=0;
//脉冲宽度
uint32_t kuandu=0;
uint16_t buffer[128]={0};
uint16_t buffer_ID=0;
//接收标志位:判断此时buffer中的数据是否已经填满
uint16_t rvcFlag=0;
char ceshi[]="start";
char yaokong;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void backword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
void forword(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
//电机后进:则将左右的IN+设置为低电平,左右的IN-设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转前进:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void left_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_SET);//right 的IN+设置为高置为高电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_RESET);//right 的IN-设置为低电平
}
//左转后退:左边的轮子不动,右边的轮子动
//左边轮子不使能,右边的轮子使能,并且将IN+设置为低电平,IN-设置为高电平
void left_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_SET);//使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_RESET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_R_IN__GPIO_Port,MOTOR_R_IN__Pin,GPIO_PIN_RESET);//right 的IN+设置为高置为低电平
HAL_GPIO_WritePin(MOTOR_R_IN_B6_GPIO_Port,MOTOR_R_IN_B6_Pin,GPIO_PIN_SET);//right 的IN-设置为高电平
}
//右转前进:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_forward(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_SET);//left 的IN+设置为高电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_RESET);//left 的IN-设置为低电平
}
//右转后退:右边的轮子不动,左边的轮子动
//右边轮子不使能,左边的轮子使能,并且将IN+设置为高电平,IN-设置为低电平
void right_back(){
//使能电机
HAL_GPIO_WritePin(MOTOR_R_S_GPIO_Port, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
HAL_GPIO_WritePin(MOTOR_L_S_GPIO_Port, MOTOR_L_S_Pin,GPIO_PIN_SET);//使能左轮子
HAL_GPIO_WritePin(MOTOR_L_IN__GPIO_Port,MOTOR_L_IN__Pin,GPIO_PIN_RESET);//left 的IN+设置为低电平
HAL_GPIO_WritePin(MOTOR_L_IN_B12_GPIO_Port,MOTOR_L_IN_B12_Pin,GPIO_PIN_SET);//left 的IN-设置为高电平
}
//停止
void stop(){
//使能电机
HAL_GPIO_WritePin(GPIOB, MOTOR_L_S_Pin,GPIO_PIN_RESET);//不使能左轮子
HAL_GPIO_WritePin(GPIOB, MOTOR_R_S_Pin,GPIO_PIN_RESET);//不使能右轮子
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//发生测试串口代码
HAL_UART_Transmit(&huart1,(uint8_t*)ceshi,strlen(ceshi),HAL_MAX_DELAY);
//使能输入捕获中断【定时器中的】
HAL_TIM_IC_Start_IT(&htim1,TIM_CHANNEL_1);
//使能定时器中断
HAL_TIM_Base_Start_IT(&htim1);
char printfbuffer[128]={0};
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if(rvcFlag){//接收到红外标志信号,表示数据接收完成
for(int i=0;i<33;i++){
//将从红外中获取的数据打印给printfbuffer
sprintf(printfbuffer," %u",buffer[i]);
//显示测试代码
//HAL_MAX_DELAY:最大超时时间
HAL_UART_Transmit(&huart1,(uint8_t*)printfbuffer,strlen(printfbuffer),HAL_MAX_DELAY);
//换行
if(i%8==0){
HAL_UART_Transmit(&huart1,"\n",2,HAL_MAX_DELAY);
}
}
//将标志位清零,等待下一次传输的数据
rvcFlag=0;
//将获取到的遥控数值转换为逻辑1或者逻辑0
for(int i=0;i<8;i++){
if(buffer[16+i]>=1400 && buffer[16+i]<1700){
yaokong |=0x01;//将其设置为逻辑1
}else if(buffer[16+i]>=400 && buffer[16+i]<=600){
yaokong |=0x00;//将其设置为逻辑0
}else {
//错误处理
}
yaokong<<=1;
}
switch(yaokong){
case 0x18://对应按键2
forword();
yaokong=0;
break;
case 0x4a://对应按键8
backword();
yaokong=0;
break;
case 0x10://对应按键4
left_forward();
yaokong=0;
break;
case 0x5a://对应按键6
right_forward();
yaokong=0;
break;
case 0x42://对应按键7
left_back();
yaokong=0;
break;
case 0x52://对应按键9
right_back();
yaokong=0;
break;
case 0x38://对应按键8
stop();
yaokong=0;
break;
}
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 16;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
//定时器的中断处理回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
//判断是否为上升沿捕获
if(isUpcat){
//将捕获的定时器的值赋值给valueUp
valueUp=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
//将上升沿置为0
isUpcat=0;//是否为上升沿的标志位
//触发捕获事件的信号边沿
__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
}else{//捕获下降沿
//将捕获的定时器的值赋值给valueDown
valueDown=HAL_TIM_ReadCapturedValue(&htim1,TIM_CHANNEL_1);
//将上升沿置为1
isUpcat=1;//是否为上升沿的标志位
//触发捕获事件的信号边沿
__HAL_TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);
//计算差值
kuandu=valueDown-valueUp;//取到了脉冲宽度
//判断输入进来的数据是否是我们想要的【判断同步码头】
if(kuandu>=4400 && kuandu <=4600){//接收码头的范围
buffer_ID=0;//这个值用来判断我遍历到数组中的哪一个位置
buffer[buffer_ID++]=kuandu;//将我们获得的数据填入
}else if(buffer_ID>0){//表示此时我们还没有填充完整
//接着往下填充
buffer[buffer_ID++]=kuandu;
if(buffer_ID>32){//表示接收完毕
rvcFlag=1;//红外接收标志位,表示数据接收完成
//将指针设置回开头
buffer_ID=0;
}
}
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
六、超声波模块:HC-SR04
1.产品特点
测试距离=高电平时间*声速340/2
2.工作原理
发送触发信号
发出8个40KHZ的脉冲
接收输出回响信号(输入捕获)
3.实物图
4.硬件接线
ECHO--->PD1 :是接收输出回响信号--->使用定时器照中的输入捕获模块(通过接收上升沿和下降沿的事件,进行事件的捕获)
5.CubeMX的设置
1.将ECHO设置为输入捕获模式
由原理图分析,知道ECHO是对应PD1,所以我们将PD1设置为输入捕获(定时器板块)
2.设置串口
3.设置用户时钟
当有物体靠近时,用户指示灯亮起
当没有物体靠近时,用户指示灯熄灭
6.代码编写
1.串口测试
cpp
//串口测试
HAL_UART_Transmit(&huart1,"go!",3,HAL_MAX_DELAY);
2.开启中断
cpp
//开启定时器输入捕获
HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);
3.编写中断处理回调函数
这里的上升沿和下降沿捕获与前面红外遥控一样
计算出一次数据接收用到的时间长度
cpp
//定时器输入捕获(回调函数)-->传输一次数据后得到的数值
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
if(isUpcat){
//获取上升沿的值
valueUp=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
//将输入捕获模式改为下降沿触发
__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
isUpcat=0;
}else{
//获取下降沿的值
valueDown=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
//将输入捕获模式改为上升沿触发
__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);
isUpcat=1;
//计算宽度(输出回响信号)
kuandu=valueDown-valueUp;
//表示数据接收完成
ready=1;
}
}
4.计算传输距离
cpp
uint32_t jisuan(uint16_t kuandu){
//此处我们/1000,是将长度单位改为毫米
//计算公式=高电平时间*340m/s(光速)/2
return kuandu*340/2/1000;
}
5.设置上升沿触发
cpp
//先将电平拉低,在拉高制造一个上升沿
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
6.将获取到的数据通过串口打印出来
cpp
while (1)
{
//判断一次数据传输接收是否完成
if(ready){
juli=jisuan(kuandu);//计算出距离
//将距离打印出来
sprintf(printfJuli," %u",juli);
//通过串口打印
HAL_UART_Transmit(&huart1,(uint8_t*)printfJuli,strlen(printfJuli),HAL_MAX_DELAY);
//记得将ready复位,才可以进行下一次数据采样
ready=0;
}
//因为我们需要先制造一次上升沿的触发信号才可以进行数据接收,所以这里一定要写这个
//先将电平拉低,在拉高制造一个上升沿
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
//设置用户指示灯
//当物体靠近设定值,则灯亮起
if(juli<=200){
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
}else{
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
7.完整代码
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "string.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t isUpcat=1;//默认输入高电平
uint16_t valueUp=0;
uint16_t valueDown=0;
//输出回响信号
uint32_t kuandu=0;
//数据接收完毕状态位
uint16_t ready=0;
//距离计算结果
uint32_t juli=0;
//将存放获得的数据
char printfJuli[124]={0};
uint32_t jisuan(uint16_t kuandu);
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
//串口测试
HAL_UART_Transmit(&huart1,"go!",3,HAL_MAX_DELAY);
//开启定时器输入捕获
HAL_TIM_IC_Start_IT(&htim17,TIM_CHANNEL_1);
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM17_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//先将电平拉低,在拉高制造一个上升沿
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
//判断一次数据传输接收是否完成
if(ready){
juli=jisuan(kuandu);//计算出距离
//将距离打印出来
sprintf(printfJuli," %u",juli);
//通过串口打印
HAL_UART_Transmit(&huart1,(uint8_t*)printfJuli,strlen(printfJuli),HAL_MAX_DELAY);
//记得将ready复位,才可以进行下一次数据采样
ready=0;
}
//因为我们需要先制造一次上升沿的触发信号才可以进行数据接收,所以这里一定要写这个
//先将电平拉低,在拉高制造一个上升沿
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_RESET);
HAL_GPIO_WritePin(TRIG_GPIO_Port,TRIG_Pin,GPIO_PIN_SET);
//设置用户指示灯
//当物体靠近设定值,则灯亮起
if(juli<=200){
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_RESET);
}else{
HAL_GPIO_WritePin(user_led_GPIO_Port,user_led_Pin,GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 16;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
//定时器输入捕获(回调函数)-->传输一次数据后得到的数值
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
if(isUpcat){
//获取上升沿的值
valueUp=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
//将输入捕获模式改为下降沿触发
__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);
isUpcat=0;
}else{
//获取下降沿的值
valueDown=HAL_TIM_ReadCapturedValue(&htim17,TIM_CHANNEL_1);
//将输入捕获模式改为上升沿触发
__HAL_TIM_SET_CAPTUREPOLARITY(&htim17,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);
isUpcat=1;
//计算宽度(输出回响信号)
kuandu=valueDown-valueUp;
//表示数据接收完成
ready=1;
}
}
uint32_t jisuan(uint16_t kuandu){
//此处我们/1000,是将长度单位改为毫米
//计算公式=高电平时间*340m/s(光速)/2
return kuandu*340/2/1000;
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
七、OLED模块:SSD1306
1.基本介绍
在强光下还是可以看得清楚
背光
2.IIC
屏幕使用IIC进行通信
【STM32】IIC(Inter Integrated Cirruit--集成电路总线)-CSDN博客
3.通信过程
4.芯片驱动:SSD1306
屏幕:128*64
是以列进行填充的
当8行数据填充满后,会自动转换到下一列
5.填充数据方法
方法1:
指令由3个数值组成=页地址(1个)+列地址(由2个组成)
2.方法二:
固定发送三个数据:第一个是页地址(行数)+第二个是固定为0x00+第三列地址(列数)
6.CubeMX设置
1.原理图分析
2.设置IIC
7.代码编写
0.指令集
【0x00】:表示要开始发送命令的指令
【0x40】:表示要开始发送数据的指令
1.初始化函数
cpp
//OLED初始化
void OLED_Init()
{
OLED_SendCmd(0xAE); /*关闭显示 display off*/
OLED_SendCmd(0x20);
OLED_SendCmd(0x10);
OLED_SendCmd(0xB0);
OLED_SendCmd(0xC8);
OLED_SendCmd(0x00);
OLED_SendCmd(0x10);
OLED_SendCmd(0x40);
OLED_SendCmd(0x81);
OLED_SendCmd(0xDF);
OLED_SendCmd(0xA1);
OLED_SendCmd(0xA6);
OLED_SendCmd(0xA8);
OLED_SendCmd(0x3F);
OLED_SendCmd(0xA4);
OLED_SendCmd(0xD3);
OLED_SendCmd(0x00);
OLED_SendCmd(0xD5);
OLED_SendCmd(0xF0);
OLED_SendCmd(0xD9);
OLED_SendCmd(0x22);
OLED_SendCmd(0xDA);
OLED_SendCmd(0x12);
OLED_SendCmd(0xDB);
OLED_SendCmd(0x20);
OLED_SendCmd(0x8D);
OLED_SendCmd(0x14);
OLED_SendCmd(0xAF); /*开启显示 display ON*/
}
2.显示数据
0x00:表示要开始发送指令
cpp
//这个是我们OLED的地址【开发方给的】
#define OLED_ADDRESS 0x78
//在显示屏上显示数据
void OLED_SendCmd(uint8_t cmd){
uint8_t SendBuffer[2];
SendBuffer[0]=0x00;//告诉他,我们要开始发送数据了【固定的】
SendBuffer[1]=cmd;
//IIC发送信息
//OLED_ADDRESS:发送者的地址
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendBuffer,strlen(SendBuffer),HAL_MAX_DELAY);
}
3.测试代码
【0x40】:表示要开始发送数据的指令
cpp
//测试函数
void OLED_test(){
//我们发送的显示的位置为:第一列的第一行
OLED_SendCmd(0xB0);//页地址(一共分为8块,1块8行)
OLED_SendCmd(0x00);//列地址(分为2份表示)
OLED_SendCmd(0x10);
//要发送的数据
//0x40:表示我们要开始发送数据【发送标志位】
//0xaa:发送的数据
char sendBuffer[]={0x40,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa};
//IIC发送信息
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,sendBuffer,strlen(sendBuffer),HAL_MAX_DELAY);
}
4.清屏函数
cpp
//存储一页数据
uint16_t GRAM[8][128]={0};
//因为我们在显示数据前应该先清屏
//要不然会出现问题
void Clear_OLED(){
for(uint8_t i=0;i<8;i++){
for(uint8_t j=0;j<128;j++){
GRAM[i][j]=0;
}
}
}
5.将显存中的数据显示到OLED上
cpp
//将显存中的数据,显示到OLED上
void OLED_ShowFrams(){
//第一个是发送的指令【表示我们要发送的是数据】
uint8_t SendSGRM[129];//存储发送的数据
SendSGRM[0]=0x40;//告诉是发送给屏幕的数据(指令)
for(uint8_t i=0;i<8;i++){
for(uint8_t j=0;j<128;j++){
//j+1:因为第一个存放了指令,后面才是存放数据
SendSGRM[j+1]=GRAM[i][j];
}
//设置页地址
OLED_SendCmd(0xb0+i);
//设置列地址(由于列地址会自己换列,所以不需要自己手动换)
OLED_SendCmd(0x00);//表示第一行
OLED_SendCmd(0x10);//表示第一列
//将数据发送给IIC
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendSGRM,strlen(SendSGRM),HAL_MAX_DELAY);
}
}
6.显示一个像素
cpp
//显示一个像素
void OLED_Setpixel(uint16_t x,uint16_t y){//x:列 y:表示行【页】
//判断是否越界
if(x>=128 || y>=64){
return;
}
//y/8:表示第几页
//y%8:表示在该页的第几行
//0x01 << (y%8):将该行置为1,其他置为0
GRAM[y/8][x] |= 0x01 << (y%8);
}
7.其他函数
cpp
//画直线
//***************************************
// * @param x1 起始点横坐标
// * @param y1 起始点纵坐标
// * @param x2 终止点横坐标
// * @param y2 终止点纵坐标
//****************************************
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
static uint8_t temp = 0;
if (x1 == x2)
{
if (y1 > y2)
{
temp = y1;
y1 = y2;
y2 = temp;
}
for (uint8_t y = y1; y <= y2; y++)
{
OLED_SetPixel(x1, y);
}
}
else if (y1 == y2)
{
if (x1 > x2)
{
temp = x1;
x1 = x2;
x2 = temp;
}
for (uint8_t x = x1; x <= x2; x++)
{
OLED_SetPixel(x, y1);
}
}
else
{
// Bresenham直线算法
int16_t dx = x2 - x1;
int16_t dy = y2 - y1;
int16_t ux = ((dx > 0) << 1) - 1;
int16_t uy = ((dy > 0) << 1) - 1;
int16_t x = x1, y = y1, eps = 0;
dx = abs(dx);
dy = abs(dy);
if (dx > dy)
{
for (x = x1; x != x2; x += ux)
{
OLED_SetPixel(x, y);
eps += dy;
if ((eps << 1) >= dx)
{
y += uy;
eps -= dx;
}
}
}
else
{
for (y = y1; y != y2; y += uy)
{
OLED_SetPixel(x, y);
eps += dx;
if ((eps << 1) >= dy)
{
x += ux;
eps -= dy;
}
}
}
}
}
//绘制矩形
//********************
// * @param x 起始点横坐标
// * @param y 起始点纵坐标
// * @param w 矩形宽度
// * @param h 矩形高度
//*********************
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
OLED_DrawLine(x, y, x + w, y);
OLED_DrawLine(x, y + h, x + w, y + h);
OLED_DrawLine(x, y, x, y + h);
OLED_DrawLine(x + w, y, x + w, y + h);
}
//绘制圆形
//********************
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
//********************
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r)
{
int16_t a = 0, b = r, di = 3 - (r << 1);
while (a <= b)
{
OLED_SetPixel(x - b, y - a);
OLED_SetPixel(x + b, y - a);
OLED_SetPixel(x - a, y + b);
OLED_SetPixel(x - b, y - a);
OLED_SetPixel(x - a, y - b);
OLED_SetPixel(x + b, y + a);
OLED_SetPixel(x + a, y - b);
OLED_SetPixel(x + a, y + b);
OLED_SetPixel(x - b, y + a);
a++;
if (di < 0)
{
di += 4 * a + 6;
}
else
{
di += 10 + 4 * (a - b);
b--;
}
OLED_SetPixel(x + a, y + b);
}
}
//绘制填充三角形
//************************
// * @param x1 第一个点横坐标
// * @param y1 第一个点纵坐标
// * @param x2 第二个点横坐标
// * @param y2 第二个点纵坐标
// * @param x3 第三个点横坐标
// * @param y3 第三个点纵坐标
//*************************
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3)
{
uint8_t a = 0, b = 0, y = 0, last = 0;
if (y1 > y2)
{
a = y2;
b = y1;
}
else
{
a = y1;
b = y2;
}
y = a;
for (; y <= b; y++)
{
if (y <= y3)
{
OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y);
}
else
{
last = y - 1;
break;
}
}
for (; y <= b; y++)
{
OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y);
}
}
//绘制一个填充圆
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r)
{
int16_t a = 0, b = r, di = 3 - (r << 1);
while (a <= b)
{
for (int16_t i = x - b; i <= x + b; i++)
{
OLED_SetPixel(i, y + a);
OLED_SetPixel(i, y - a);
}
for (int16_t i = x - a; i <= x + a; i++)
{
OLED_SetPixel(i, y + b);
OLED_SetPixel(i, y - b);
}
a++;
if (di < 0)
{
di += 4 * a + 6;
}
else
{
di += 10 + 4 * (a - b);
b--;
}
}
}
//*****************************
// * @brief 绘制一个椭圆
// * @param x 椭圆中心横坐标
// * @param y 椭圆中心纵坐标
// * @param a 椭圆长轴
// * @param b 椭圆短轴
//******************************
void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b)
{
int xpos = 0, ypos = b;
int a2 = a * a, b2 = b * b;
int d = b2 + a2 * (0.25 - b);
while (a2 * ypos > b2 * xpos)
{
OLED_SetPixel(x + xpos, y + ypos);
OLED_SetPixel(x - xpos, y + ypos);
OLED_SetPixel(x + xpos, y - ypos);
OLED_SetPixel(x - xpos, y - ypos);
if (d < 0)
{
d = d + b2 * ((xpos << 1) + 3);
xpos += 1;
}
else
{
d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2);
xpos += 1, ypos -= 1;
}
}
d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2;
while (ypos > 0)
{
OLED_SetPixel(x + xpos, y + ypos);
OLED_SetPixel(x - xpos, y + ypos);
OLED_SetPixel(x + xpos, y - ypos);
OLED_SetPixel(x - xpos, y - ypos);
if (d < 0)
{
d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3);
xpos += 1, ypos -= 1;
}
else
{
d = d + a2 * (-(ypos << 1) + 3);
ypos -= 1;
}
}
}
8.完整代码
1.oled.c
cpp
#include "oled.h"
#include "string.h"
//这个是我们OLED的地址【开发方给的】
#define OLED_ADDRESS 0x78
//存储一页数据
uint16_t GRAM[8][128]={0};
//在显示屏上显示数据
void OLED_SendCmd(uint8_t cmd){
uint8_t SendBuffer[2];
SendBuffer[0]=0x00;//告诉他,我们要开始发送数据了【固定的】
SendBuffer[1]=cmd;
//IIC发送信息
//OLED_ADDRESS:发送者的地址
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendBuffer,strlen(SendBuffer),HAL_MAX_DELAY);
}
//OLED初始化
void OLED_Init()
{
OLED_SendCmd(0xAE); /*关闭显示 display off*/
OLED_SendCmd(0x20);
OLED_SendCmd(0x10);
OLED_SendCmd(0xB0);
OLED_SendCmd(0xC8);
OLED_SendCmd(0x00);
OLED_SendCmd(0x10);
OLED_SendCmd(0x40);
OLED_SendCmd(0x81);
OLED_SendCmd(0xDF);
OLED_SendCmd(0xA1);
OLED_SendCmd(0xA6);
OLED_SendCmd(0xA8);
OLED_SendCmd(0x3F);
OLED_SendCmd(0xA4);
OLED_SendCmd(0xD3);
OLED_SendCmd(0x00);
OLED_SendCmd(0xD5);
OLED_SendCmd(0xF0);
OLED_SendCmd(0xD9);
OLED_SendCmd(0x22);
OLED_SendCmd(0xDA);
OLED_SendCmd(0x12);
OLED_SendCmd(0xDB);
OLED_SendCmd(0x20);
OLED_SendCmd(0x8D);
OLED_SendCmd(0x14);
OLED_SendCmd(0xAF); /*开启显示 display ON*/
}
//测试函数
void OLED_test(){
//我们发送的显示的位置为:第一列的第一行
OLED_SendCmd(0xB0);//页地址(一共分为8块,1块8行)
OLED_SendCmd(0x00);//列地址(分为2份表示)
OLED_SendCmd(0x10);
//要发送的数据
//0x40:表示我们要开始发送数据【发送标志位】
//0xaa:发送的数据
uint8_t sendBuffer[]={0x40,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa};
//IIC发送信息
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,sendBuffer,strlen(sendBuffer),HAL_MAX_DELAY);
}
//因为我们在显示数据前应该先清屏
//要不然会出现问题
void Clear_OLED(){
for(uint8_t i=0;i<8;i++){
for(uint8_t j=0;j<128;j++){
GRAM[i][j]=0;
}
}
}
//将显存中的数据,显示到OLED上
void OLED_ShowFrams(){
//第一个是发送的指令【表示我们要发送的是数据】
uint8_t SendSGRM[129];//存储发送的数据
SendSGRM[0]=0x40;//告诉是发送给屏幕的数据(指令)
for(uint8_t i=0;i<8;i++){
for(uint8_t j=0;j<128;j++){
//j+1:因为第一个存放了指令,后面才是存放数据
SendSGRM[j+1]=GRAM[i][j];
}
//设置页地址
OLED_SendCmd(0xb0+i);
//设置列地址(由于列地址会自己换列,所以不需要自己手动换)
OLED_SendCmd(0x00);//表示第一行
OLED_SendCmd(0x10);//表示第一列
//将数据发送给IIC
HAL_I2C_Master_Transmit(&hi2c1,OLED_ADDRESS,SendSGRM,strlen(SendSGRM),HAL_MAX_DELAY);
}
}
//显示一个像素
void OLED_Setpixel(uint16_t x,uint16_t y){//x:列 y:表示行【页】
//判断是否越界
if(x>=128 || y>=64){
return;
}
//y/8:表示第几页
//y%8:表示在该页的第几行
//0x01 << (y%8):将该行置为1,其他置为0
GRAM[y/8][x] |= 0x01 << (y%8);
}
//画直线
//***************************************
// * @param x1 起始点横坐标
// * @param y1 起始点纵坐标
// * @param x2 终止点横坐标
// * @param y2 终止点纵坐标
//****************************************
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
static uint8_t temp = 0;
if (x1 == x2)
{
if (y1 > y2)
{
temp = y1;
y1 = y2;
y2 = temp;
}
for (uint8_t y = y1; y <= y2; y++)
{
OLED_SetPixel(x1, y);
}
}
else if (y1 == y2)
{
if (x1 > x2)
{
temp = x1;
x1 = x2;
x2 = temp;
}
for (uint8_t x = x1; x <= x2; x++)
{
OLED_SetPixel(x, y1);
}
}
else
{
// Bresenham直线算法
int16_t dx = x2 - x1;
int16_t dy = y2 - y1;
int16_t ux = ((dx > 0) << 1) - 1;
int16_t uy = ((dy > 0) << 1) - 1;
int16_t x = x1, y = y1, eps = 0;
dx = abs(dx);
dy = abs(dy);
if (dx > dy)
{
for (x = x1; x != x2; x += ux)
{
OLED_SetPixel(x, y);
eps += dy;
if ((eps << 1) >= dx)
{
y += uy;
eps -= dx;
}
}
}
else
{
for (y = y1; y != y2; y += uy)
{
OLED_SetPixel(x, y);
eps += dx;
if ((eps << 1) >= dy)
{
x += ux;
eps -= dy;
}
}
}
}
}
//绘制矩形
//********************
// * @param x 起始点横坐标
// * @param y 起始点纵坐标
// * @param w 矩形宽度
// * @param h 矩形高度
//*********************
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
OLED_DrawLine(x, y, x + w, y);
OLED_DrawLine(x, y + h, x + w, y + h);
OLED_DrawLine(x, y, x, y + h);
OLED_DrawLine(x + w, y, x + w, y + h);
}
//绘制圆形
//********************
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
//********************
void OLED_DrawCircle(uint8_t x, uint8_t y, uint8_t r)
{
int16_t a = 0, b = r, di = 3 - (r << 1);
while (a <= b)
{
OLED_SetPixel(x - b, y - a);
OLED_SetPixel(x + b, y - a);
OLED_SetPixel(x - a, y + b);
OLED_SetPixel(x - b, y - a);
OLED_SetPixel(x - a, y - b);
OLED_SetPixel(x + b, y + a);
OLED_SetPixel(x + a, y - b);
OLED_SetPixel(x + a, y + b);
OLED_SetPixel(x - b, y + a);
a++;
if (di < 0)
{
di += 4 * a + 6;
}
else
{
di += 10 + 4 * (a - b);
b--;
}
OLED_SetPixel(x + a, y + b);
}
}
//绘制填充三角形
//************************
// * @param x1 第一个点横坐标
// * @param y1 第一个点纵坐标
// * @param x2 第二个点横坐标
// * @param y2 第二个点纵坐标
// * @param x3 第三个点横坐标
// * @param y3 第三个点纵坐标
//*************************
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3)
{
uint8_t a = 0, b = 0, y = 0, last = 0;
if (y1 > y2)
{
a = y2;
b = y1;
}
else
{
a = y1;
b = y2;
}
y = a;
for (; y <= b; y++)
{
if (y <= y3)
{
OLED_DrawLine(x1 + (y - y1) * (x2 - x1) / (y2 - y1), y, x1 + (y - y1) * (x3 - x1) / (y3 - y1), y);
}
else
{
last = y - 1;
break;
}
}
for (; y <= b; y++)
{
OLED_DrawLine(x2 + (y - y2) * (x3 - x2) / (y3 - y2), y, x1 + (y - last) * (x3 - x1) / (y3 - last), y);
}
}
//绘制一个填充圆
// * @param x 圆心横坐标
// * @param y 圆心纵坐标
// * @param r 圆半径
void OLED_DrawFilledCircle(uint8_t x, uint8_t y, uint8_t r)
{
int16_t a = 0, b = r, di = 3 - (r << 1);
while (a <= b)
{
for (int16_t i = x - b; i <= x + b; i++)
{
OLED_SetPixel(i, y + a);
OLED_SetPixel(i, y - a);
}
for (int16_t i = x - a; i <= x + a; i++)
{
OLED_SetPixel(i, y + b);
OLED_SetPixel(i, y - b);
}
a++;
if (di < 0)
{
di += 4 * a + 6;
}
else
{
di += 10 + 4 * (a - b);
b--;
}
}
}
//*****************************
// * @brief 绘制一个椭圆
// * @param x 椭圆中心横坐标
// * @param y 椭圆中心纵坐标
// * @param a 椭圆长轴
// * @param b 椭圆短轴
//******************************
void OLED_DrawEllipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b)
{
int xpos = 0, ypos = b;
int a2 = a * a, b2 = b * b;
int d = b2 + a2 * (0.25 - b);
while (a2 * ypos > b2 * xpos)
{
OLED_SetPixel(x + xpos, y + ypos);
OLED_SetPixel(x - xpos, y + ypos);
OLED_SetPixel(x + xpos, y - ypos);
OLED_SetPixel(x - xpos, y - ypos);
if (d < 0)
{
d = d + b2 * ((xpos << 1) + 3);
xpos += 1;
}
else
{
d = d + b2 * ((xpos << 1) + 3) + a2 * (-(ypos << 1) + 2);
xpos += 1, ypos -= 1;
}
}
d = b2 * (xpos + 0.5) * (xpos + 0.5) + a2 * (ypos - 1) * (ypos - 1) - a2 * b2;
while (ypos > 0)
{
OLED_SetPixel(x + xpos, y + ypos);
OLED_SetPixel(x - xpos, y + ypos);
OLED_SetPixel(x + xpos, y - ypos);
OLED_SetPixel(x - xpos, y - ypos);
if (d < 0)
{
d = d + b2 * ((xpos << 1) + 2) + a2 * (-(ypos << 1) + 3);
xpos += 1, ypos -= 1;
}
else
{
d = d + a2 * (-(ypos << 1) + 3);
ypos -= 1;
}
}
}
2.main.c
cpp
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "i2c.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "oled.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
//OLED初始化
HAL_Delay(20);
OLED_Init();
// Clear_OLED();
// OLED_ShowFrams();
OLED_test();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
for(uint8_t i=0;i<8;i++){
Clear_OLED();
OLED_Setpixel(64,32);
OLED_ShowFrams();
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV1;
RCC_OscInitStruct.PLL.PLLN = 8;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
八,综合项目
1.模块
红外避障
红外循迹
红外遥控
串口通信
2.CubeMX设置
1.超声波
2.红外遥控部分
3.串口通信
4.红外避障
5.红外循迹
6.电机
7.OLED
OLED使用到IIC通信,所以使用IIC引脚设置
8.串口
9.时钟配置
3.问题解决
当我们添加新的.c或者.h文件记得将其添加进去
本项目源码:
code/project/project · 林何/STM32G0xx小车 - 码云 - 开源中国 (gitee.com)
存在问题
1)电机循迹,避障部分--->左右单独无法进行正常显示