自己diy的一個小項目,還有很多可以開發!
Altium Designer畫的原理圖和PCB圖如下:(51hei附件中可下載工程文件)
51hei.png (71.97 KB, 下載次數: 13)
下載附件
2024-8-30 00:26 上傳
51hei.png (36.39 KB, 下載次數: 5)
下載附件
2024-8-30 00:26 上傳
單片機源程序如下:
- /* Includes ------------------------------------------------------------------*/
- #include "main.h"
- #include "adc.h"
- #include "i2c.h"
- #include "rtc.h"
- #include "tim.h"
- #include "usart.h"
- #include "gpio.h"
- /* Private includes ----------------------------------------------------------*/
- /* USER CODE BEGIN Includes */
- #include "oled.h" // OLED顯示屏頭文件
- #include "stdio.h" // 標準輸入輸出頭文件
- #include "rtc.h" // 實時時鐘頭文件
- #include "led.h" // LED控制頭文件
- #include "key.h" // 按鍵控制頭文件
- #include "ds18B20.h" // DS18B20溫度傳感器頭文件
- #include "eeprom.h" // EEPROM存儲器頭文件
- //#include "adc0832.h" // ADC0832模數轉換器頭文件
- #include "string.h" // 字符串處理頭文件
- /* USER CODE END Includes */
- /* Private typedef -----------------------------------------------------------*/
- /* USER CODE BEGIN PTD */
- /* USER CODE END PTD */
- /* Private define ------------------------------------------------------------*/
- /* USER CODE BEGIN PD */
- #define SAMPLE_COUNT 20 // 采樣次數,調整此值以改變采樣率
- uint8_t screen = 0; // 當前顯示的界面:0表示第一個界面,1表示第二個界面,2表示第三個界面
- /* USER CODE END PD */
- /* Private macro -------------------------------------------------------------*/
- /* USER CODE BEGIN PM */
- /* USER CODE END PM */
- /* Private variables ---------------------------------------------------------*/
- /* USER CODE BEGIN PV */
- RTC_DateTypeDef GetData; // 獲取日期結構體
- RTC_TimeTypeDef GetTime; // 獲取時間結構體
- uint32_t key_Tick; // 按鍵時間標志
- uint16_t adc_value; // ADC讀取值
- uint8_t saveEEPROM = 0; // 保存EEPROM標志
- uint8_t showEEPROM = 0; // 顯示EEPROM標志
- uint8_t eepromDisplayed = 0; // 新增變量,用于標記EEPROM值是否已顯示
- uint8_t measureInProgress = 0; // 測量進行中的標志
- uint16_t highPressure = 0; // 高壓值
- uint16_t lowPressure = 0; // 低壓值
- uint16_t adc_values[SAMPLE_COUNT]; // ADC采集的值數組
- /* 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 KEY_Process() // 按鍵功能處理函數
- {
- if (uwTick - key_Tick < 20) return; // 防抖處理,避免誤觸發
- key_Tick = uwTick; // 記錄當前時間
- Key_Read(); // 讀取按鍵狀態
- if (Trg & 0x01) // 按鍵1按下
- {
- HAL_UART_Transmit(&huart3, (uint8_t*)"B", 1, 50); // 通過串口發送字符'B',語音播報:測量中,不要移動和說話
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);
- screen = 1; // 切換到第2個界面
- measureInProgress = 1; // 開始測量
- eepromDisplayed = 0; // 重置EEPROM顯示標記
- }
- if (Trg & 0x02) // 按鍵2按下
- {
- screen = 2; // 切換到第3個界面
- HAL_UART_Transmit(&huart3, (uint8_t*)"C", 1, 50);
- showEEPROM = 1; // 設置顯示EEPROM標志
- eepromDisplayed = 0; // 重置EEPROM顯示標記
- }
- if (Trg & 0x04) // 按鍵3按下
- {
- OLED_Clear();
- screen = 0; // 切換到第1個界面
- HAL_UART_Transmit(&huart3, (uint8_t*)"A", 1, 50); // 通過串口發送字符'A',語音播報:歡迎使用便攜式電子血壓計
- }
- if (Trg & 0x08) // 按鍵4按下
- {
- OLED_Clear();
- screen = 3; // 切換到第1個界面
- HAL_UART_Transmit(&huart3, (uint8_t*)"F", 1, 50); // 通過串口發送字符'F',語音播報:時間播報
- }
- }
- void ADC_Process(uint8_t is_high_pressure)
- {
- uint32_t adc_sum = 0;
- uint16_t max_value = 0;
- uint16_t min_value = 0xFFFF;
- for (int i = 0; i < SAMPLE_COUNT; i++) // 采樣SAMPLE_COUNT次
- {
- HAL_ADC_Start(&hadc1); // 啟動ADC
- HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY); // 等待轉換完成
- uint16_t value = HAL_ADC_GetValue(&hadc1); // 獲取ADC值
- adc_values[i] = value; // 存儲ADC值
- adc_sum += value; // 累加ADC值
- if (value > max_value) max_value = value; // 更新最大值
- if (value < min_value) min_value = value; // 更新最小值
- HAL_ADC_Stop(&hadc1); // 停止ADC
- HAL_Delay(100); // 100ms延時
-
- // Count++;
- // if(Count >= 80)
- // {
- // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);//氣閥開始放氣
- // Count = 0;
- // }
- }
-
- // 使用去掉最大值和最小值后的均值
- adc_sum -= max_value + min_value;
- uint16_t average_value = adc_sum / (SAMPLE_COUNT - 2);
-
- // float voltage = average_value * 3.3 / 4095.0;
- float voltage = average_value * 3.3 / 1150.0;
- float pressure = ((voltage - 0.5) * 300.0) / 4.0;
- if (is_high_pressure)
- {
- highPressure = (int)(pressure * 1.2); // 保存高壓值
- // highPressure = average_value; // 保存高壓值
- }
- else
- {
- lowPressure = (int)(pressure * 0.8); // 保存低壓值
- // lowPressure = average_value; // 保存低壓值
- }
- }
- /*
- void Measure_Pressure()
- {
- measureInProgress = 1; // 開始測量
-
- if(measureInProgress == 1)
- {
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);//氣閥開始充氣
-
- for (int i = 0; i < 5; i++) // 總共采集5次數據
- {
- if (i < 3) // 前3次采集低壓值
- {
- ADC_Process(0);
- }
- else // 后2次采集高壓值
- {
- ADC_Process(1);
- }
- }
- measureInProgress = 0; // 測量完成
- }
- // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET);//氣閥開始充氣
-
- // for (int i = 0; i < 5; i++) // 總共采集5次數據
- // {
- // if (i < 3) // 前3次采集低壓值
- // {
- // ADC_Process(0);
- // }
- // else // 后2次采集高壓值
- // {
- //
- // ADC_Process(1);
- //
- // }
- // }
- // measureInProgress = 0; // 測量完成
- if(measureInProgress == 0)
- {
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);//氣閥開始放氣
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
- if((highPressure >= 85) && (highPressure <= 130) && (lowPressure >= 60) && (lowPressure <= 85))
- {
- HAL_UART_Transmit(&huart1, (uint8_t*)"D", 1, 50);
- }
- else
- {
- HAL_UART_Transmit(&huart1, (uint8_t*)"E", 1, 50);
- }
- }
- }
- */
- void Measure_Pressure()
- {
- measureInProgress = 1; // 開始測量
- uint32_t startTick = HAL_GetTick(); // 記錄開始時間
- int lowPressureSamples = 0; // 低壓樣本計數
- int highPressureSamples = 0; // 高壓樣本計數
- // 前6秒充氣
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_SET); // 開始充氣
- while ((HAL_GetTick() - startTick) < 6000) // 在7秒內
- {
- if (lowPressureSamples < 3) // 前3次采集低壓值
- {
- ADC_Process(0);
- lowPressureSamples++;
- }
- else if (highPressureSamples < 2) // 后2次采集高壓值
- {
- ADC_Process(1);
- highPressureSamples++;
- }
- }
- // 停止充氣
- HAL_GPIO_WritePin(GPIOC, GPIO_PIN_6, GPIO_PIN_RESET);
- // 如果6秒內沒有采集到足夠的數據,繼續采集直到滿足條件
- while (lowPressureSamples < 3 || highPressureSamples < 2)
- {
- if (lowPressureSamples < 3) // 前3次采集低壓值
- {
- ADC_Process(0);
- lowPressureSamples++;
- }
- else if (highPressureSamples < 2) // 后2次采集高壓值
- {
- ADC_Process(1);
- highPressureSamples++;
- }
- }
- // 等待最后2秒放氣
- while ((HAL_GetTick() - startTick) < 7000) // 總共6秒
- {
- // 等待
- }
- // 最后2秒放氣
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 開始放氣
- // 等待測量完成
- while ((HAL_GetTick() - startTick) < 10000) // 總共10秒
- {
- // 等待
- }
- measureInProgress = 0; // 測量完成
- // 檢查血壓值并發送相應的串口消息
- if((highPressure >= 85) && (highPressure <= 130) && (lowPressure >= 60) && (lowPressure <= 85))
- {
- HAL_UART_Transmit(&huart3, (uint8_t*)"D", 1, 50);
- LED1_Off();
- LED2_Off();
- LED3_On();
- }
- else
- {
- HAL_UART_Transmit(&huart3, (uint8_t*)"E", 1, 50);
- LED1_Off();
- LED2_On();
- LED3_Off();
- }
- }
- void Save_Pressure_To_EEPROM()
- {
- uint8_t highPressureLow = highPressure & 0xFF;
- uint8_t highPressureHigh = (highPressure >> 8) & 0xFF;
- At24c02_Write_Byte(0x00, &highPressureLow);
- At24c02_Write_Byte(0x01, &highPressureHigh);
- uint8_t lowPressureLow = lowPressure & 0xFF;
- uint8_t lowPressureHigh = (lowPressure >> 8) & 0xFF;
- At24c02_Write_Byte(0x02, &lowPressureLow);
- At24c02_Write_Byte(0x03, &lowPressureHigh);
- saveEEPROM = 0;
- }
- void Show_EEPROM_On_OLED()
- {
- uint8_t highPressureLow = 0, highPressureHigh = 0;
- uint8_t lowPressureLow = 0, lowPressureHigh = 0;
- At24c02_Read_Byte(0x00, &highPressureLow);
- At24c02_Read_Byte(0x01, &highPressureHigh);
- uint16_t savedHighPressure = (highPressureHigh << 8) | highPressureLow;
- At24c02_Read_Byte(0x02, &lowPressureLow);
- At24c02_Read_Byte(0x03, &lowPressureHigh);
- uint16_t savedLowPressure = (lowPressureHigh << 8) | lowPressureLow;
- char highPressureString[20];
- char lowPressureString[20];
- snprintf(highPressureString, sizeof(highPressureString), "High: %u mmHg", savedHighPressure);
- snprintf(lowPressureString, sizeof(lowPressureString), "Low : %u mmHg", savedLowPressure);
- OLED_ShowString(2, 2, highPressureString, 16, 0);
- OLED_ShowString(2, 4, lowPressureString, 16, 0);
- showEEPROM = 0;
- }
- //串口重定義函數
- int fputc(int ch, FILE *f)
- {
- HAL_UART_Transmit(&huart3, (unsigned char*)ch, 1, 50); // 通過串口發送字符
- return ch; // 返回字符
- }
- /* 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();
- MX_RTC_Init();
- MX_TIM1_Init();
- MX_TIM6_Init();
- MX_USART3_UART_Init();
- MX_ADC1_Init();
- /* USER CODE BEGIN 2 */
- OLED_Init(); // 初始化OLED顯示屏
- LED_Init(); // 初始化LED
- DS18B20_Init(); // 初始化DS18B20溫度傳感器
-
- /* 設置RTC時間 */
- RTC_set_time(24, 6, 15, 16, 22, 00, RTC_FORMAT_BIN);
-
- HAL_UART_Transmit(&huart3, (uint8_t*)"A", 1, 50); // 通過串口發送字符'A',語音播報:歡迎使用便攜式電子血壓計
-
- /* USER CODE END 2 */
- /* Infinite loop */
- /* USER CODE BEGIN WHILE */
- while (1)
- {
- /* USER CODE END WHILE */
- /* USER CODE BEGIN 3 */
- if (HAL_GetTick() % 300 == 0) // 每300ms執行一次
- {
- if (screen == 0) // 顯示時間和日期的界面
- {
- // HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN); // 獲取日期
- // HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN); // 獲取時間
- // char timeString[20];
- // snprintf(timeString, sizeof(timeString), "Time: %02d:%02d:%02d", GetTime.Hours, GetTime.Minutes, GetTime.Seconds); // 格式化時間字符串
- // char dateString[20];
- // snprintf(dateString, sizeof(dateString), "Date: %02d-%02d-%02d", GetData.Year, GetData.Month, GetData.Date); // 格式化日期字符串
- // 第一行顯示12x12大小的漢字
- OLED_ShowCHinese(2, 0, 0, 12, 0); // 顯示漢字"歡"
- OLED_ShowCHinese(14, 0, 1, 12, 0); // 顯示漢字"迎"
- OLED_ShowCHinese(26, 0, 2, 12, 0); // 顯示漢字"使"
- OLED_ShowCHinese(38, 0, 3, 12, 0); // 顯示漢字"用"
- OLED_ShowCHinese(50, 0, 4, 12, 0); // 顯示漢字"便"
- OLED_ShowCHinese(62, 0, 5, 12, 0); // 顯示漢字"攜"
- OLED_ShowCHinese(74, 0, 6, 12, 0); // 顯示漢字"式"
- OLED_ShowCHinese(86, 0, 7, 12, 0); // 顯示漢字"血"
- OLED_ShowCHinese(98, 0, 8, 12, 0); // 顯示漢字"壓"
- OLED_ShowCHinese(110, 0, 9, 12, 0); // 顯示漢字"計"
- // 第二行顯示16x16大小的漢字
- OLED_ShowCHinese(2, 2, 0, 16, 0); // 顯示漢字"溫"
- OLED_ShowCHinese(18, 2, 1, 16, 0); // 顯示漢字"度"
- OLED_ShowCHinese(34, 2, 2, 16, 0); // 顯示漢字"值"
- OLED_ShowCHinese(98, 2, 3, 16, 0); // 顯示漢字"℃"
- float temperature = DS18B20_Get_Temperature(); // 獲取溫度
- char tempString[20];
- snprintf(tempString, sizeof(tempString), ":%.2f", temperature); // 格式化溫度字符串
- OLED_ShowString(50, 2, tempString, 16, 0); // 顯示溫度
- char pressureString[20];
- snprintf(pressureString, sizeof(pressureString), "000mmHg");
- OLED_ShowString(2, 4, pressureString, 16, 0); // 顯示默認的血壓值
- }
- else if (screen == 1) // 顯示測量血壓的界面
- {
- if (measureInProgress) // 測量進行中
- {
- LED1_On();
- HAL_Delay(200);
- LED1_Off();
- HAL_Delay(200);
- HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_7);
- OLED_Clear();
- OLED_ShowCHinese(40, 2, 4, 16, 0); // 顯示"測量中"的中文字符
- OLED_ShowCHinese(56, 2, 5, 16, 0);
- OLED_ShowCHinese(72, 2, 6, 16, 0);
-
-
- Measure_Pressure(); // 調用測量血壓函數,一直等待測量,測量完才進行下列代碼
- OLED_Clear();
- OLED_ShowCHinese(2, 2, 7, 16, 0); // 顯示高壓和低壓的中文字符
- OLED_ShowCHinese(18, 2, 8, 16, 0);
- OLED_ShowCHinese(2, 4, 9, 16, 0);
- OLED_ShowCHinese(18, 4, 10, 16, 0);
- char highPressureString[20];
- snprintf(highPressureString, sizeof(highPressureString), ": %u mmHg", highPressure); // 格式化高壓字符串
- OLED_ShowString(36, 2, highPressureString, 16, 0); // 顯示高壓
- char lowPressureString[20];
- snprintf(lowPressureString, sizeof(lowPressureString), ": %u mmHg", lowPressure); // 格式化低壓字符串
- OLED_ShowString(36, 4, lowPressureString, 16, 0); // 顯示低壓
- Save_Pressure_To_EEPROM(); // 保存血壓值到EEPROM
- eepromDisplayed = 1; // 標記EEPROM已顯示
- }
- }
- else if (screen == 2) // 顯示EEPROM數據的界面
- {
- if (!eepromDisplayed) // 如果EEPROM數據尚未顯示
- {
- OLED_Clear();
- Show_EEPROM_On_OLED(); // 顯示EEPROM中的血壓數據
- eepromDisplayed = 1; // 標記EEPROM已顯示
- }
- }
- else if(screen == 3)
- {
- HAL_RTC_GetDate(&hrtc, &GetData, RTC_FORMAT_BIN); // 獲取當前日期
- HAL_RTC_GetTime(&hrtc, &GetTime, RTC_FORMAT_BIN); // 獲取當前時間
-
- char timeString[20];
- snprintf(timeString, sizeof(timeString), "Time: %02d:%02d:%02d", GetTime.Hours, GetTime.Minutes, GetTime.Seconds); // 格式化時間字符串
- char dateString[20];
- snprintf(dateString, sizeof(dateString), "Date: %02d-%02d-%02d", GetData.Year, GetData.Month, GetData.Date); // 格式化日期字符串
-
- /* 顯示界面 */
- OLED_ShowCHinese(28, 0, 12, 16, 0); // 顯示“時”
- OLED_ShowCHinese(44, 0, 13, 16, 0); // 顯示“間”
- OLED_ShowCHinese(60, 0, 14, 16, 0); // 顯示“顯”
- OLED_ShowCHinese(76, 0, 15, 16, 0); // 顯示“示”
- OLED_ShowString(5, 4, timeString, 16, 0); // 顯示時間
- OLED_ShowString(5, 2, dateString, 16, 0); // 顯示日期
- }
- }
- KEY_Process(); // 按鍵處理
- }
- /* USER CODE END 3 */
- }
- /**
- * @brief System Clock Configuration
- * @retval None
- */
- void SystemClock_Config(void)
- {
- RCC_OscInitTypeDef RCC_OscInitStruct = {0};
- RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
- RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
- /** Initializes the RCC Oscillators according to the specified parameters
- * in the RCC_OscInitTypeDef structure.
- */
- RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
- RCC_OscInitStruct.HSEState = RCC_HSE_ON;
- RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
- RCC_OscInitStruct.LSEState = RCC_LSE_ON;
- RCC_OscInitStruct.HSIState = RCC_HSI_ON;
- RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
- RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
- RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
- if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
- {
- ……………………
- …………限于本文篇幅 余下代碼請從51黑下載附件…………
復制代碼
資料51hei附件下載:
程序源碼.7z
(436.66 KB, 下載次數: 22)
2024-8-30 00:28 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
PCB Project.7z
(3.41 MB, 下載次數: 24)
2024-8-30 00:28 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|