洪嵐峰 發表於 2023-4-28 19:05:06

STM32F103C8T6

製作 STM32F103C8T6 數控可調直流穩壓電源恒壓恒流
需要以下零件:

STM32F103C8T6 微控制器
16MHz 晶振
2 個 22pF 陶瓷電容
AMS1117-3.3 3.3V 穩壓器
2 個 470uF 電解電容
2 個 0.1uF 陶瓷電容
2 個 5K 歐姆電阻器
1 個 10K 歐姆電阻器
2 個 2K 歐姆電阻器
1 個 10uF 電解電容
2 個 LED(紅色和綠色)
4 個按鈕
LCD1602 顯示屏(帶 I2C 介面)
IRF540N MOSFET
10K 可變電阻
2 個 BNC 接口
杜邦線
以下是連結腳位的圖表:

STM32F103C8T6        AMS1117-3.3
3.3V        VIN
GND        GND
PA1        ADJ
PA2        OUT
STM32F103C8T6        IRF540N
PB0        Gate
GND        Source
PA2        Drain
STM32F103C8T6        LCD1602
PB8        SCL
PB9        SDA
STM32F103C8T6        LED
PB12        紅色
PB13        綠色
STM32F103C8T6        按鈕
PA4        UP
PA5        DOWN
PA6        SET
PA7        ON/OFF
STM32F103C8T6        BNC
PB14        CH1
PB15        CH2

以下是 STM32F103C8T6 的程式碼:


#include "stm32f10x.h"
#include "delay.h"
#include "lcd1602.h"

#define ADC_RESOLUTION 4096

#define B1 GPIO_Pin_4
#define B2 GPIO_Pin_5
#define B3 GPIO_Pin_6
#define B4 GPIO_Pin_7

#define LED_GREEN GPIO_Pin_13
#define LED_RED GPIO_Pin_12

#define CH1_PIN GPIO_Pin_14
#define CH2_PIN GPIO_Pin_15

#define ADC_CHANNEL ADC_Channel_1
#define ADC_SAMPLETIME ADC_SampleTime_7Cycles5

#define PWM_FREQUENCY 20000
#define PWM_PERIOD 840
#define PWM_DUTY_CYCLE_MAX 839

#define VREF 3.3
#define VOUT_MAX 30
#define IOUT_MAX 5
#define LCD_LINE1 0
#define LCD_LINE2 16

float vOutSet = 0.0;
float iOutSet = 0.0;

float vOutMeasured = 0.0;
float iOutMeasured = 0.0;

uint16_t adcValue = 0;

void initGPIO(void);
void initADC(void);
void initPWM(void);
void initLCD(void);
void readADC(void);
void displayLCD(void);

int main(void)
{
initGPIO();
initADC();
initPWM();
initLCD();

while (1)
{
    readADC();
    displayLCD();
}
}

void initGPIO(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = B1 | B2 | B3 | B4;
GPIO_Init(GPIOA, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = LED_RED | LED_GREEN;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = CH1_PIN | CH2_PIN;
GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}

void initADC(void)
{
ADC_InitTypeDef ADC_InitStructure;

RCC_ADCCLKConfig(RCC_PCLK2_Div8);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

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 = 1;
ADC_Init(ADC1, &ADC_InitStructure);

ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SAMPLETIME);

ADC_Cmd(ADC1, ENABLE);

ADC_ResetCalibration(ADC1);

while (ADC_GetResetCalibrationStatus(ADC1))
    ;

ADC_StartCalibration(ADC1);

while (ADC_GetCalibrationStatus(ADC1))
    ;
}

void initPWM(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseStructure.TIM_Period = PWM_PERIOD;
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / (PWM_FREQUENCY * PWM_PERIOD) - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);

TIM_Cmd(TIM1, ENABLE);
}

void initLCD(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

I2C_InitTypeDef I2C_InitStructure;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = LCD_SCL | LCD_SDA;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);

I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &I2C_InitStructure);

I2C_Cmd(I2C1, ENABLE);

LCD_Init();
LCD_Clear();
LCD_WriteString("V: 00.00 I: 00.00");
LCD_SetCursor(0, LCD_LINE2);
LCD_WriteString("VSet: 00.00 ISet: 00.00");
}

void readADC(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE);

while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
    ;

adcValue = ADC_GetConversionValue(ADC1);

vOutMeasured = (float)adcValue * V_REF / ADC_RESOLUTION * VOLTAGE_DIVIDER_RATIO;
iOutMeasured = (vOutSet - vOutMeasured) / CURRENT_SENSE_RESISTOR;
}

void displayLCD(void)
{
char str;

sprintf(str, "V: %.2f I: %.2f ", vOutMeasured, iOutMeasured);
LCD_SetCursor(0, 0);
LCD_WriteString(str);

sprintf(str, "VSet: %.2f ISet: %.2f", vOutSet, iOutSet);
LCD_SetCursor(0, LCD_LINE2);
LCD_WriteString(str);
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
while (1)
;
}
#endif

#ifdef USE_STDPERIPH_DRIVER
void SysTick_Handler(void)
{
TimingDelay_Decrement();
}

void Delay(__IO uint32_t nTime)
{
TimingDelay = nTime;

while (TimingDelay != 0)
    ;
}
#endif

#pragma GCC diagnostic pop

// ----------------------------------------------------------------------------
// 文件結束
// ----------------------------------------------------------------------------

讓我們來逐行解析一下這個程式碼:

// 設定使用的 STM32 MCU 為 STM32F103C8T6
#include "stm32f10x.h"

// ----------------------------------------------------------------------------
// 定義
// ----------------------------------------------------------------------------

// 顯示 LCD 的 I2C 位址
#define SLAVE_ADDRESS_LCD 0x3F

// LCD 的寬度和高度
#define LCD_WIDTH 16
#define LCD_HEIGHT 2

// LCD 的兩行位址
#define LCD_LINE1 0x00
#define LCD_LINE2 0x40

// LCD 的 SCL 和 SDA 位址
#define LCD_SCL GPIO_Pin_6
#define LCD_SDA GPIO_Pin_7

// ADC 輸入的參數
#define V_REF 3.3
#define ADC_RESOLUTION 4096
#define VOLTAGE_DIVIDER_RATIO 2.0
#define CURRENT_SENSE_RESISTOR 0.2

// ----------------------------------------------------------------------------
// 全局變量
// ----------------------------------------------------------------------------

// 電源輸出的目標電壓和目標電流
float vOutSet = 0.0;
float iOutSet = 0.0;

// 電源輸出的實際電壓和電流
float vOutMeasured = 0.0;
float iOutMeasured = 0.0;

// ADC 讀取的值
uint16_t adcValue = 0;

// ----------------------------------------------------------------------------
// 函數原型
// ----------------------------------------------------------------------------

void initGPIO(void);
void initI2C(void);
void initADC(void);
void initLCD(void);
void setLCDByte(uint8_t);
void sendLCDCommand(uint8_t);
void sendLCDData(uint8_t);
void LCD_Init(void);
void LCD_Clear(void);
void LCD_SetCursor(uint8_t, uint8_t);
void LCD_WriteString(char *);
void readADC(void);
void displayLCD(void);

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *, uint32_t);
#endif

// ----------------------------------------------------------------------------
// 主功能
// ----------------------------------------------------------------------------

int main(void)
{
    // 初始化 GPIO、I2C、ADC 和 LCD
    initGPIO();
    initI2C();
    initADC();
    initLCD();

    // 設定電源輸出的目標電壓和目標電流
    vOutSet = 5.0;
    iOutSet = 0.1;

    while (1)
    {
        // 讀取 ADC 訊號
        readADC();

        // 顯示目前電壓和電流
        displayLCD();
    }
}

// ----------------------------------------------------------------------------
// 實現函數
// ----------------------------------------------------------------------------

// 初始化 GPIO 位址
void initGPIO(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 啟用 GPIOB 的時鐘
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    // 設定 SCL 和 SDA 為開漏輸出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Pin = LCD_SCL | LCD_SDA;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

// 初始化 I2C 位址
void initI2C(void)
{
    I2C_InitTypeDef I2C_InitStructure;

    // 啟用 I2C1 的時鐘
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

// 設定 I2C 位址和速度
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x00;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_Init(I2C1, &I2C_InitStructure);

// 啟用 I2C1
I2C_Cmd(I2C1, ENABLE);
}

// 初始化 ADC 位址
void initADC(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;

// 啟用 GPIOA 的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

// 設定 PA0 為 ADC 輸入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 啟用 ADC1 的時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

// 設定 ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);

// 啟用 ADC1 的通道 0
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);

// 啟用 ADC1
ADC_Cmd(ADC1, ENABLE);
}

// 初始化 LCD 位址
void initLCD(void)
{
// 初始化 LCD
LCD_Init();

// 清除 LCD
LCD_Clear();

// 設定 LCD 的游標
LCD_SetCursor(0, 0);

// 顯示 "Voltage: "
LCD_WriteString("Voltage: ");

// 設定 LCD 的游標
LCD_SetCursor(1, 0);

// 顯示 "Current: "
LCD_WriteString("Current: ");
}

// 設定 LCD 的位元
void setLCDByte(uint8_t byte)
{
I2C_SendData(I2C1, byte);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}

// 傳送 LCD 的命令
void sendLCDCommand(uint8_t command)
{
uint8_t upperNibble = command & 0xF0;
uint8_t lowerNibble = (command << 4) & 0xF0;

// 傳送上位 4 個位元組
setLCDByte(upperNibble | 0x00);
setLCDByte(upperNibble | 0x04);
setLCDByte(upperNibble | 0x00);

// 傳送下位 4 個位元
setLCDByte(lowerNibble | 0x00);
setLCDByte(lowerNibble | 0x04);
setLCDByte(lowerNibble | 0x00);

// 命令之間需要等待一段時間
delay_ms(2);
}

// 傳送 LCD 的資料
void sendLCDData(uint8_t data)
{
uint8_t upperNibble = data & 0xF0;
uint8_t lowerNibble = (data << 4) & 0xF0;

// 傳送上位 4 個位元組
setLCDByte(upperNibble | 0x01);
setLCDByte(upperNibble | 0x05);
setLCDByte(upperNibble | 0x01);

// 傳送下位 4 個位元組
setLCDByte(lowerNibble | 0x01);
setLCDByte(lowerNibble | 0x05);
setLCDByte(lowerNibble | 0x01);

// 命令之間需要等待一段時間
delay_ms(2);
}

// 將數字轉換為字元
void intToChar(uint16_t num, char* str)
{
uint8_t i = 0;
do
{
str = (num % 10) + '0';
num /= 10;
} while (num != 0);

str = '\0';

// 反轉字串
for (uint8_t j = 0; j < i / 2; j++)
{
    char temp = str;
    str = str;
    str = temp;
}
}

int main(void)
{
// 初始化時鐘
SystemInit();

// 初始化 I2C
initI2C();

// 初始化 ADC
initADC();

// 初始化 LCD
initLCD();

// 轉換數字為字元的暫存區
char tempStr;

while (1)
{
    // 讀取 ADC 的值
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
    uint16_t adcValue = ADC_GetConversionValue(ADC1);

    // 計算電壓和電流
    float voltage = (adcValue * 3.3f / 4095.0f) * 4.3f;
    float current = (voltage - 2.5f) / 0.185f;

    // 將電壓和電流顯示在 LCD 上
    LCD_SetCursor(0, 9);
    intToChar((uint16_t)(voltage * 1000), tempStr);
    LCD_WriteString(tempStr);
    LCD_WriteString(" mV");

    LCD_SetCursor(1, 9);
    intToChar((uint16_t)(current * 1000), tempStr);
    LCD_WriteString(tempStr);
    LCD_WriteString(" mA");

    // 命令之間需要等待一段時間
    delay_ms(500);
}
}
頁: [1]
查看完整版本: STM32F103C8T6