- UID
- 373967
- 帖子
- 8844
- 主題
- 2609
- 精華
- 0
- 積分
- 999
- 楓幣
- 0
- 威望
- 976
- 存款
- 34672
- 贊助金額
- 0
- 推廣
- 0
- GP
- 1205
- 閱讀權限
- 50
- 在線時間
- 453 小時
- 註冊時間
- 2023-1-12
- 最後登入
- 2024-11-17
|
對於這樣一個項目,您需要以下零件:
STM32F103C8T6微控制器
直流輸入電壓:12-24V
2x16字符LCD顯示屏
2個旋鈕
1個數字鍵盤
1個步進馬達
1個L298N雙H橋驅動器
1個可變電阻
1個LM2596 DC-DC轉換器
數個小型電阻、電容、二極管和連接器
以下是STM32F103C8T6微控制器的引腳連接:
PB0:步進馬達IN1
PB1:步進馬達IN2
PB10:步進馬達IN3
PB11:步進馬達IN4
PC0:數字鍵盤列1
PC1:數字鍵盤列2
PC2:數字鍵盤列3
PC3:數字鍵盤列4
PC4:數字鍵盤行1
PC5:數字鍵盤行2
PC6:數字鍵盤行3
PC7:數字鍵盤行4
PA0:旋鈕1
PA1:旋鈕2
PA2:LCD D4
PA3:LCD D5
PA4:LCD D6
PA5:LCD D7
PA6:LCD EN
PA7:LCD RS
這是程式碼示例
使用STM32F103C8T6來控制步進馬達
顯示電壓和電流於LCD顯示屏
並通過旋鈕和數字鍵盤設置電源的電壓和電流。
Current(current_setpoint);
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_tim.h"
#include "misc.h"
#include "lcd.h"
#define ADC1_DR_Address ((uint32_t)0x4001244C)
// 定義步進馬達的4個控制引腳
#define MOTOR_IN1 GPIO_Pin_0
#define MOTOR_IN2 GPIO_Pin_1
#define MOTOR_IN3 GPIO_Pin_10
#define MOTOR_IN4 GPIO_Pin_11
// 定義旋鈕和數字鍵盤的引腳
#define KNOB_1 GPIO_Pin_0
#define KNOB_2 GPIO
// ADC通道和數值
#define ADC_CHANNEL_VOLTAGE ADC_Channel_1
#define ADC_CHANNEL_CURRENT ADC_Channel_2
uint16_t ADC_Value[2];
// 預設設置
#define DEFAULT_VOLTAGE 12.0
#define DEFAULT_CURRENT 1.0
// PWM設置
#define PWM_TIM TIM4
#define PWM_FREQ 20000 // 20kHz
#define PWM_PERIOD 1000 // 1kHz
// LCD設置
#define LCD_RS GPIO_Pin_7
#define LCD_EN GPIO_Pin_6
#define LCD_D4 GPIO_Pin_2
#define LCD_D5 GPIO_Pin_3
#define LCD_D6 GPIO_Pin_4
#define LCD_D7 GPIO_Pin_5
// 全局變量
float voltage_setpoint = DEFAULT_VOLTAGE;
float current_setpoint = DEFAULT_CURRENT;
float voltage = 0.0;
float current = 0.0;
// 函數原型
void GPIO_Configuration(void);
void ADC_Configuration(void);
void TIM_Configuration(void);
void PWM_Configuration(void);
void LCD_Configuration(void);
void SetVoltage(float v);
void SetCurrent(float c);
void UpdateDisplay(void);
int main(void)
{
// 初始化
GPIO_Configuration();
ADC_Configuration();
TIM_Configuration();
PWM_Configuration();
LCD_Configuration();
SetVoltage(DEFAULT_VOLTAGE);
SetCurrent(DEFAULT_CURRENT);
// 主循環
while (1)
{
// 讀取旋鈕和數字鍵盤,並設置電壓和電流設置點
uint16_t knob1 = GPIO_ReadInputDataBit(GPIOA, KNOB_1);
uint16_t knob2 = GPIO_ReadInputDataBit(GPIOA, KNOB_2);
if (knob1 == 0)
{
voltage_setpoint += 0.1;
if (voltage_setpoint > 24.0)
voltage_setpoint = 24.0;
SetVoltage(voltage_setpoint);
}
else if (knob2 == 0)
{
voltage_setpoint -= 0.1;
if (voltage_setpoint < 0.0)
voltage_setpoint = 0.0;
SetVoltage(voltage_setpoint);
}
// 讀取數字鍵盤
uint8_t key = 0;
uint8_t i, j;
GPIO_ResetBits(GPIOC, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
for (i = 0; i < 4; i++)
{
GPIO_SetBits(GPIOC, GPIO_Pin_4 << i);
for (j = 0; j < 4; j++)
{
if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_0 << j) == 0)
{
key = i * 4 + j + 1;
break;
}
}
GPIO_ResetBits(GPIOC, GPIO_Pin_4 << i);
}
// 根據按鍵設置電壓和電流設置點
if (key != 0)
{
switch (key)
{
case 10: // *
voltage_setpoint = 0.0;
SetVoltage(voltage_setpoint);
break;
case 11: // #
current_setpoint = 0.0;
SetCurrent(current_setpoint);
break;
case 12: // A
voltage_setpoint = DEFAULT_VOLTAGE;
SetVoltage(voltage_setpoint);
break;
case 13: // B
current_setpoint = DEFAULT_CURRENT;
SetCurrent(current_setpoint);
break;
case 14: // C
voltage_setpoint += 1.0;
if (voltage_setpoint > 24.0)
voltage_setpoint = 24.0;
SetVoltage(voltage_setpoint);
break;
case 15: // D
voltage_setpoint -= 1.0;
if (voltage_setpoint < 0.0)
voltage_setpoint = 0.0;
SetVoltage(voltage_setpoint);
break;
case 16: // E
current_setpoint += 0.1;
if (current_setpoint > 5.0)
current_setpoint = 5.0;
SetCurrent(current_setpoint);
break;
case 1: // 1
current_setpoint -= 0.1;
if (current_setpoint < 0.0)
current_setpoint = 0.0;
SetCurrent(current_setpoint);
break;
}
}
// 更新顯示
UpdateDisplay();
// 延遲一段時間
delay_ms(50);
}
}
// 設置GPIO
void GPIO_Configuration(void)
{
// 啟用GPIOA、GPIOB、GPIOC、GPIOD時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
// 設置PA0和PA1為模擬輸入
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 設置PB8和PB9為模擬輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 設置PA5和PA6為PWM輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 設置PB0和PB1為LCD輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
// 設置PC0-PC3為數字鍵盤輸入,PC4-PC7為數字鍵盤輸出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed
GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 設置PD2為按鈕輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
// 設置TIM3
void TIM3_Configuration(void)
{
// 啟用TIM3時鐘
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
// 設置TIM3的計時參數
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; // 分頻器,頻率為72MHz/72=1MHz
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 計數值為1000,計數器周期為1000us
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 不分頻
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上計數
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 設置TIM3的PWM模式
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 輸出使能
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比為0
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高有效
TIM_OC1Init(TIM3, &TIM_OCInitStructure); // 設置通道1
TIM_OC2Init(TIM3, &TIM_OCInitStructure); // 設置通道2
// 啟用TIM3的PWM輸出
TIM_Cmd(TIM3, ENABLE);
}
// 設置ADC
void ADC_Configuration(void)
{
// 啟用ADC1時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
// 設置ADC1的輸入通道
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; // 獨立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; // 單通道模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 連續模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 不使用外部觸發
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; // 數據右對齊
ADC_InitStructure.ADC_NbrOfChannel = 2; // 轉換通道數量為2
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1,
ADC_Channel_11, 1, ADC_SampleTime_71Cycles5); // 設置通道11,採樣時間為71.5個週期
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 2, ADC_SampleTime_71Cycles5); // 設置通道12,採樣時間為71.5個週期
// 啟用ADC1
ADC_Cmd(ADC1, ENABLE);
// 開始ADC1的校準
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
}
int main(void)
{
// 初始化時鐘
RCC_Configuration();
// 初始化GPIO
GPIO_Configuration();
// 初始化TIM3
TIM3_Configuration();
// 初始化ADC
ADC_Configuration();
// 設置初始占空比為50%
TIM_SetCompare1(TIM3, 500);
TIM_SetCompare2(TIM3, 500);
// 循環執行
while (1)
{
// 如果按鈕被按下,則增加占空比
if (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_2) == RESET)
{
if (TIM_GetCapture1(TIM3) < 950)
{
TIM_SetCompare1(TIM3, TIM_GetCapture1(TIM3) + 50);
TIM_SetCompare2(TIM3, TIM_GetCapture2(TIM3) + 50);
delay_ms(100);
}
}
// 如果按鈕被釋放,則減少占空比
else
{
if (TIM_GetCapture1(TIM3) > 50)
{
TIM_SetCompare1(TIM3, TIM_GetCapture1(TIM3) - 50);
TIM_SetCompare2(TIM3, TIM_GetCapture2(TIM3) - 50);
delay_ms(100);
}
}
// 讀取ADC的轉換值
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
uint16_t adc_value1 = ADC_GetConversionValue(ADC1);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
uint16_t adc_value2 = ADC_GetConversionValue(ADC1);
// 將轉換值轉換為電壓值,並顯示在LCD上
float voltage1 = adc_value1 / 4096.0 * 3.3;
float voltage2 = adc_value2 / 4096.0 * 3.3;
lcd_gotoxy(0, 0);
lcd_printf("Voltage 1: %5.2fV", voltage1);
lcd_gotoxy(0, 1);
lcd_printf("Voltage 2: %5.2fV", voltage2);
// 延時10ms
delay_ms(10);
}
}
程式碼的解釋:
RCC_Configuration()
此函數用於初始化系統時鐘,使系統時鐘頻率為72MHz。
GPIO_Configuration()
此函數用於初始化GPIO引腳,將TIM3的輸出引腳和按鈕的輸入引腳設置為正確的模式和初始狀態。
TIM3_Configuration()
此函數用於初始化TIM3,設置TIM3為PWM模式並設置脈衝寬度。
ADC_Configuration()
此函數用於初始化ADC1,設置ADC1的採樣時間和通道,並啟用ADC1。
main()
主函數包含以下幾個步驟:
(1) 初始化時鐘、GPIO、TIM3和ADC。
(2) 設置初始占空比為50%。
(3) 在while循環中,不斷檢測按鈕是否被按下,如果被按下,則增加占空比,如果被釋放,則減少占空比。同時,讀取ADC轉換值,將其轉換為電壓值,並在LCD上顯示出來。
(4) 延時10ms。
整個程式的作用是控制PWM輸出的占空比,從而控制電壓的大小,同時讀取ADC轉換值,將其轉換為電壓值,並在LCD上顯示出來。
通過按鈕可以調節PWM輸出的占空比,從而調節電壓的大小。
LCD初始化
在程式碼中,使用了一個名為LCD_Init()的函數來初始化LCD模塊。
在函數中,首先將LCD的控制線設置為輸出模式,然後將RS和EN控制線設置為低電平。
接著,分別向LCD寫入0x30和0x20,用於初始化LCD模塊。
最後,根據LCD模塊的規格,向其寫入命令來設置LCD的顯示模式、顯示內容和顯示位置等參數。
LCD顯示
在程式碼中,使用了一個名為LCD_ShowString()的函數來在LCD上顯示字符串。
在函數中,首先向LCD寫入命令,用於設置LCD顯示位置。
然後逐個將字符串中的字符轉換為ASCII碼,再向LCD寫入ASCII碼,從而將字符串顯示在LCD上。
按鈕檢測
在程式碼中,使用了一個名為Button_Scan()的函數來檢測按鈕是否被按下。
在函數中,首先讀取按鈕的狀態,如果按鈕被按下,則延時一段時間,確認按鈕的狀態是否穩定,如果穩定,則返回按鈕的編號。
如果按鈕沒有被按下,則返回0。
PWM輸出
在程式碼中,使用了一個名為TIM_SetCompare2()的函數來設置TIM3的比較值,從而控制PWM輸出的占空比。
在函數中,首先將TIM3的CCR2寄存器設置為指定的值,從而改變PWM輸出的占空比。 |
|