平衡車 - 電機調速

🌈個人主頁:羽晨同學

💫個人格言:“成為自己未來的主人~”?

在我們的這篇文章當中,我們主要想要實現的功能的是電機調速功能。在我們的這篇文章中,主要實現的是開環的功能,而非閉環,也就是不加入PID對電機速度進行控制。

首先,我們需要將STBY引腳設置為高電平,因為STBY為休眠引腳,當STBY為低電平的時候,電機處于休眠狀態,此時電機是不會進行運動的。

由電路圖可知,STBY引腳,也就是PA1引腳,所以,我們需要將PA1設置為高電平。

在這個之前,我們先實現按鍵的功能,通過按鍵控制電機速度。

static Button_TypeDef userKey;//用戶按鈕
static void OnUserKey_Clicked(uint8_t clicks);
//
// @簡介: 按鍵任務的初始化函數
//
void App_Button_Init(void)
{Button_InitTypeDef Button_InitStruct = {0};Button_InitStruct.GPIOx = GPIOA;Button_InitStruct.GPIO_Pin = GPIO_Pin_11;My_Button_Init(&userKey,&Button_InitStruct);My_Button_SetClickCb(&userKey,OnUserKey_Clicked);
}

通過回調函數來控制按鍵按下之后的操作。

//
// @簡介: 按鍵回調函數
//
static void OnUserKey_Clicked(uint8_t clicks)
{}

所以,接下來,我們通過回調函數來實現對應的功能

首先,我們先設置STBY,來控制是休眠狀態還是啟動狀態,也就是說,我們需要先配置PA1引腳。

//
// @簡介: 初始化STBY引腳 PA1 -- Out -PP
//
static void STBY_Pin_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);}

然后,我們設置一個函數來控制STBY的狀態

//
// @簡介: 控制TB6612進入休眠狀態或者活動狀態
// @參數: on  0 - 休眠狀態,向STBY寫L
// 					 非零 - 活動狀態,向STBY寫H
//
void App_Pwm_Cmd(uint8_t on)
{if(on == 0){GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);//休眠}else{GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);//覺醒}
}

這樣的話,我們就可以在按鍵回調函數當中,通過按下按鍵來改變對應的狀態

//
// @簡介: 按鍵回調函數
//
static void OnUserKey_Clicked(uint8_t clicks)
{if(clicks == 1){pwm_on^=1;App_Pwm_Cmd(pwm_on);}
}

所以,這個時候來說,我們就實現了,只要按下按鍵,就可以控制STBY的狀態。

然后,我們在main中對PWM進行初始化

#include "stm32f10x.h"
#include "delay.h"
#include "app_bat.h"
#include "app_button.h"
int main(void)
{NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);App_Bat_Init();App_Button_Init();App_Pwm_Init();while(1){App_Bat_Proc();App_Button_Proc();}
}

這樣,就可以實現通過按鍵來控制STBY的狀態了。

接下來,我們就來實現電機調速的具體功能。

這個是電機控制的具體的引腳圖,我們可以看到在這個H橋的結構當中,AIN1為正,AIN2為負的時候,是正轉,否則反轉,所以,我們可以通過控制AIN1和AIN2來控制電機轉動的方向,通過PWMA和PWMB來控制速度,而對應的AIN1和AIN2為PA10和PA9,同理,BIN1和BIN2為PB5和PB7,所以,首先,我們需要對這些引腳進行初始化。

//
// @簡介: 左電機的初始化
//
static void Motor_L_Init(void)
{//初始化 PA9 PA10為Out_PPRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);
}
//
// @簡介: 右電機初始化
//
static void Motor_R_Init(void)
{//初始化PB5和PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);
}

這樣,我們就對對應的引腳進行了初始化

接下來,我們需要對對應的PWM進行初始化,一個是PA8一個是PB6,我們應該設置為AF_PP模式,因為PA8和PB6均被定時器所占用,所以為復用模式,又因為要輸出高低電平,所以為推挽模式。

//
// @簡介: 左電機的初始化
//
static void Motor_L_Init(void)
{//初始化 PA9 PA10為Out_PPRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);//對PWM進行初始化,PA8 - AF_PPGPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);
}
//
// @簡介: 右電機初始化
//
static void Motor_R_Init(void)
{//初始化PB5和PB7RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);//對PWM進行初始化,PB6 - AF_PPGPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB,&GPIO_InitStruct);}

初始化完成之后,接下來,讓我們完成對應的PWM的操作

這個是定時器內部的結構框圖。

我們先來完成時基單元的參數設置,我們想要實現的效果為1000級可調,我們輸入的是72MHz,為了讓周期盡可能的小,所以我們的PSC為0,為了實現1000級可調,PWM是由CCR寄存器實現的,它的范圍是0-ARR寄存器,所以,ARR應該為999,重復計數器RCR為0就好了,接下來,我們在代碼中實現這個功能:

所以,我們對定時器進行初始化

	//對定時器1進行初始化RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);//設置時基單元的參數TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period = 999;TIM_TimeBaseInitStruct.TIM_Prescaler = 0;TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStruct);
	//設置時基單元的參數TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStruct.TIM_Period = 999;TIM_TimeBaseInitStruct.TIM_Prescaler = 0;TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStruct);	

初始化時鐘之后,我們對PWM進行設置,PWM是在CCR中進行設置,通過輸出比較產生PWM波

	//配置輸出比較TIM_OCInitTypeDef TIM_OCInitStruct = {0};TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStruct.TIM_OutputState = ENABLE;TIM_OCInitStruct.TIM_Pulse = 0;TIM_OC1Init(TIM1,&TIM_OCInitStruct);//配置MOE的開關TIM_CtrlPWMOutputs(TIM1,ENABLE);//閉合定時器的總開關TIM_Cmd(TIM1,ENABLE);
	//配置輸出比較TIM_OCInitTypeDef TIM_OCInitStruct = {0};TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStruct.TIM_OutputState = ENABLE;TIM_OCInitStruct.TIM_Pulse = 0;TIM_OC1Init(TIM4,&TIM_OCInitStruct);//配置MOE的開關TIM_CtrlPWMOutputs(TIM4,ENABLE);//閉合定時器的總開關TIM_Cmd(TIM4,ENABLE);

接下來,我們來設置左右電機的占空比

//
// @簡介: 控制左電機的PWM
// @參數: duty 占空比的具體指,-100.0f - 100.0f
//
void App_Pwm_Set_L(float duty)
{float sign;//符號,正數 - +1,負數, - 1if(duty >= 0) sign = 1;else sign = -1;duty = fabs(duty);if(sign >= 0)//正傳{GPIO_WriteBit(GPIOA,GPIO_Pin_9,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOA,GPIO_Pin_10,Bit_RESET);//AIN2 - 低}else//反轉{GPIO_WriteBit(GPIOA,GPIO_Pin_10,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOA,GPIO_Pin_9,Bit_RESET);//AIN2 - 低	}uint16_t ccr = duty/100.0f*999;TIM_SetCompare1(TIM1,ccr);
}
//
// @簡介: 控制右電機的PWM
// @參數: duty 占空比的具體指,-100.0f - 100.0f
//
void App_Pwm_Set_R(float duty)
{float sign;//符號,正數 - +1,負數, - 1if(duty >= 0) sign = 1;else sign = -1;duty = fabs(duty);if(sign >= 0)//正傳{GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOB,GPIO_Pin_7,Bit_RESET);//AIN2 - 低}else//反轉{GPIO_WriteBit(GPIOB,GPIO_Pin_7,Bit_SET);// AIN1 - 高GPIO_WriteBit(GPIOB,GPIO_Pin_5,Bit_RESET);//AIN2 - 低	}uint16_t ccr = duty/100.0f*999;TIM_SetCompare1(TIM4,ccr);
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/97857.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/97857.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/97857.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從利潤率看價值:哪些公司值得長期持有?

💡 為什么盯緊利潤率? 投資者常常盯著營收增長,卻忽略了一個更關鍵的指標——利潤率。 收入可以靠規模“堆”出來,但利潤率卻是企業護城河的真實體現。心理學研究表明:當一個產品或服務被消費者認定為“不可替代”&a…

小迪web自用筆記25

傳統文件上傳:上傳至服務器本身硬盤。云存儲:借助云存儲oss對象存儲(只能被訪問,不可解析)Oss云存儲Access key與Access ID:有了這兩個東西之后就可以操作云存儲,可以向里面發數據了。這玩意兒泄…

分發餅干——很好的解釋模板

好的,孩子,我們來玩一個“喂餅干”的游戲。 0. 問題的本質是什么? 想象一下,你就是個超棒的家長,手里有幾塊大小不一的餅干,而面前有幾個餓著肚子的小朋友。每個小朋友都有一個最小的“胃口”值&#xff0c…

場景題:如果一個大型項目,某一個時間所有的CPU的已經被占用了,導致服務不可用,我們開發人員應該如何使服務器盡快恢復正常

問:如果一個大型項目,某一個時間所有的CPU的 已經被占用了,導致服務不可用,我們開發人員 應該如何使服務器盡快恢復正常答:應對CPU 100%導致服務不可用的緊急恢復流程面試官,如果遇到這種情況,我會立即按照…

Docker 安裝 RAGFlow保姆教程

前提條件 Ubuntu 服務器(20.04 或 22.04 LTS 推薦) 已安裝 Docker 和 Docker Compose 如果尚未安裝,請先運行以下命令:# 安裝 Docker curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh # 將當前用戶加入 docker 組,避免每次都要 sudo sudo user…

為什么實際工程里 C++ 部署深度學習模型更常見?為什么大家更愛用 TensorRT?

很多人剛接觸深度學習模型部署的時候,都會習慣用 Python,因為訓練的時候就是 PyTorch、TensorFlow 啊,寫起來方便。但一到 實際工程,特別是工業設備、醫療影像、上位機系統這種場景,你會發現大多數人都轉向了 C 部署。…

深入理解 Java 集合框架:底層原理與實戰應用

在日常開發中,集合是 Java 中使用頻率最高的工具之一。從最常見的 ArrayList、HashMap 到更復雜的并發集合,幾乎每一個 Java 程序員都離不開集合框架。集合框架不僅提供了豐富的數據結構實現,還封裝了底層復雜的邏輯,讓開發者能夠…

爬取m3u8視頻完整教程

爬取步驟:1.先找到網頁源代碼2.從網頁源代碼中拿到m3u83.下載m3u84.讀取m3u8文件,下載視頻5.合并視頻首先我們來爬取一個星辰影院的電影:下面我以這個為例:我們需要在源代碼中找到m3u8這個url:緊接著我們利用下面的方法…

Python爬蟲實戰: 基于Scrapy的Amazon跨境電商選品數據爬蟲方案

概述與設計思路 利用Python的Scrapy框架進行大規模頁面抓取和結構化數據提取,配合aiohttp實現高并發請求,從而高效獲取Amazon平臺上的商品列表、詳情、評論等公開信息。通過對這些數據進行清洗與分析,可以識別出有潛力的商品,評估市場競爭程度,并跟蹤競爭對手的動態,為跨…

穩定版IM即時通訊 仿默往APP即時通訊im源碼聊天社交源碼支持二開原生開發獨立部署 含搭建教程

內容目錄一、詳細介紹二、效果展示1.部分代碼2.效果圖展示三、學習資料下載一、詳細介紹 技術開發語言: 后臺管理端:Java GO Mysql數據庫 安卓端:Java iOS端:ob PC端:c 功能簡單介紹: 單聊&#xff…

封裝一個redis獲取并解析數據的工具類

redis獲取并解析數據工具類實現代碼使用示例實現代碼 import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.TypeReference; import lom…

23種設計模式——策略模式 (Strategy Pattern)?詳解

?作者簡介:大家好,我是 Meteors., 向往著更加簡潔高效的代碼寫法與編程方式,持續分享Java技術內容。 🍎個人主頁:Meteors.的博客 💞當前專欄:設計模式 ?特色專欄:知識分享 &#x…

CI(持續集成)、CD(持續交付/部署)、CT(持續測試)、CICD、CICT

目錄 **CI、CD、CT 詳解與關系** **1. CI(Continuous Integration,持續集成)** **2. CD(Continuous Delivery/Deployment,持續交付/部署)** **持續交付(Continuous Delivery)** **持續部署(Continuous Deployment)** **3. CT(Continuous Testing,持續測試)** **4.…

【音視頻】WebRTC ICE 模塊深度剖析

原文鏈接: https://mp.weixin.qq.com/s?__bizMzIzMjY3MjYyOA&mid2247498075&idx2&sn6021a2f60b1e7c71ce4d7af6df0b9b89&chksme893e540dfe46c56323322e780d41aec1f851925cfce8b76b3f4d5cfddaa9c7cbb03a7ae4c25&scene178&cur_album_id314699…

linux0.12 head.s代碼解析

重新設置IDT和GDT,為256個中斷門設置默認的中斷處理函數檢查A20地址線是否啟用設置數學協處理器將main函數相關的參數壓棧設置分頁機制,將頁表映射到0~16MB的物理內存上返回main函數執行 源碼詳細注釋如下: /** linux/boot/head.s** (C) 1991 Linus T…

Maven動態控制版本號秘籍:高效發包部署,版本管理不再頭疼!

作者:唐叔在學習 專欄:唐叔的Java實踐 關鍵詞:Maven版本控制、versions插件、動態版本號、持續集成、自動化部署、Java項目管理 摘要:本文介紹如何使用Maven Versions插件動態控制項目版本號和依賴組件版本號,實現無需…

簡述:普瑞時空數據建庫軟件(國土變更建庫)之一(變更預檢查部分規則)

簡述:普瑞時空數據建庫軟件(國土變更建庫)之一(變更預檢查部分規則) 主要包括三種類型:常規檢查、行政區范圍檢查、20X異常滅失檢查 本blog地址:https://blog.csdn.net/hsg77

shell中命令小工具:cut、sort、uniq,tr的使用方式

提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔 文章目錄前言一、cut —— 按列或字符截取1. 常用選項2. 示例二、sort —— 排序(默認按行首字符升序)1. 常用選項常用 sort 命令選項三、uniq —— 去…

【Linux】Linux開發必備:Git版本控制與GDB調試全指南

前言:在Linux開發流程中,版本控制與程序調試是保障項目穩定性和開發效率的兩大核心環節。Git作為當前最主流的分布式版本控制系統,能高效管理代碼迭代、追蹤修改記錄并支持多人協同開發;GDB(GNU調試器)是Li…

實現 TypeScript 內置工具類型(源碼解析與實現)

目標讀者:已經熟悉 TypeScript 基礎語法、泛型、條件類型的同學。本文按常見工具類型的分類與順序實現并解釋 Partial、Required、Readonly、Pick、Omit、Record、Exclude、Extract、NonNullable、ReturnType、Parameters、ConstructorParameters、InstanceType、Th…