56_fputc()和fgetc()

本文介绍了一个简单的C程序实例,演示如何利用fputc()函数将转换为大写的字符写入文件,并通过fgetc()函数从文件读取字符。程序首先提示用户输入一个以‘.’结尾的字符串,然后将字符串中的小写字母转换为大写并保存到文件中,最后再从文件中读取这些字符并展示出来。
//_56_fputc()和fgetc()
//_56_main.cpp

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int main()
{
	FILE *fp;//文件指针变量
	char str[100];
	fp=fopen("file.txt","w");//!!!!!!!!!!是w不是r了记得
	if(fp==NULL)
	{
		printf("无法打开文件.\n");
		exit(0);
	}
	printf("请输入一个字符串(以“.”结尾):\n");
	gets(str);
	printf("%s\n",str);
	//将字符串中的小写字符转换成大写字符,直到遇到“.”为止
	for(int i=0;str[i]!='.';i++)
	{
		if(str[i]>='a'&&str[i]<='z')
			str[i] = toupper(str[i]);
		fputc(str[i],fp);//将转换后的字符存入文件
	}
	fclose(fp);
	fp=fopen("file.txt","r");
	for(int i=0;str[i]!='.';i++)
		str[i] = fgetc(fp);//将文件中的字符回传到str中去
	fclose(fp);
	printf("%s\n",str);

	system("pause");
	return 0;
}

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 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 "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "math.h" #include "font6x8.h" #include "sdd1306.h" #include "key.h" #include "stdio.h" #include "vofa.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ int a,b,c,d; float f=0.5;float f1=1;float f2=2;float z=0; float t=3.14/4; int n =1; uint16_t current_freq = 100; uint16_t current_duty = 50; #define K1 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) #define K2 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) #define BUFFER_SIZE 64 uint8_t rx_byte; // 当前接收到的字节 char rx_buffer[BUFFER_SIZE]; // 接收缓冲区 uint16_t rx_index = 0; // 当前写入位置 /* 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 */ void ProcessReceivedString(char *input); static uint32_t key_last_time = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//中断回调 { if (huart == &huart3) { // 如果收到的是 '\n' 或 '\r',表示命令结束 if (rx_byte == '\n' || rx_byte == '\r') { if (rx_index > 0) { rx_buffer[rx_index] = '\0'; // 结束字符串 // 处理接收到的内容 ProcessReceivedString(rx_buffer); // 清空缓冲区 rx_index = 0; } } else { // 普通字符加入缓冲区(防溢出) if (rx_index < BUFFER_SIZE - 1) { rx_buffer[rx_index++] = rx_byte; } } // 重新开启下一次接收 HAL_UART_Receive_IT(&huart3, &rx_byte, 1); } } void ProcessReceivedString(char *input) { char output[128]; // 构造输出:"xx is ok !" snprintf(output, sizeof(output), "%s is ok !", input); // 发送回上位机 printf("Command received: %s\r\n", input); // 如果有 OLED 屏幕,也在屏幕上显示 OLED_Clear(); OLED_ShowString(0, 16, output,1); OLED_UpdateScreen(); } static void BreathingLight(void){ float t1 = HAL_GetTick()*0.001; float duty1 = 0.5 * sin(f*3.14*t1+a*t/f)+0.5; uint16_t arr1 = __HAL_TIM_GET_AUTORELOAD(&htim1); uint16_t ccr1 = duty1 * (arr1+1); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,ccr1); float t2 = HAL_GetTick()*0.001; float duty2 = 0.5 * sin(f*3.14*t2+b*t/f)+0.5; uint16_t arr2 = __HAL_TIM_GET_AUTORELOAD(&htim2); uint16_t ccr2 = duty2* (arr2+1); __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,ccr2); float t3 = HAL_GetTick()*0.001; float duty3 = 0.5 * sin(f*3.14*t3+c*t/f)+0.5; uint16_t arr3 = __HAL_TIM_GET_AUTORELOAD(&htim3); uint16_t ccr3 = duty3 * (arr3+1); __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,ccr3); float t4 = HAL_GetTick()*0.001; float duty4 = 0.5 * sin(f*3.14*t4+d*t/f)+0.5; uint16_t arr4 = __HAL_TIM_GET_AUTORELOAD(&htim4); uint16_t ccr4 = duty4 * (arr4+1); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_3,ccr4); } //static void UpdateOLEDDisplay(void) //{ // uint32_t psc = htim1.Init.Prescaler; // uint32_t arr = htim1.Init.Period; // // float timer_clock_freq = (float)HAL_RCC_GetPCLK2Freq(); // float freq = timer_clock_freq / ((arr + 1) * (psc + 1)); // // // 3. 获取TIM1通道1的比较值(CCR) // uint32_t ccr = __HAL_TIM_GET_COMPARE(&htim1, TIM_CHANNEL_2); // // // 4. 计算占空比 // float duty_cycle = (float)ccr / (arr + 1) * 100.0f; // 转换为百分比 // char display_buf[32]; // // // 3. 将数值格式化为字符串 // sprintf(display_buf, "Freq: %.1f Hz", freq); // OLED_ShowString(0, 0, (uint8_t*)display_buf, 16); // // // sprintf(display_buf, "Duty: %.1f %%", duty_cycle); // OLED_ShowString(0, 8, (uint8_t*)display_buf, 16); // // // OLED_DrawSquareWave(0, 32, 16, freq, duty_cycle); // // OLED_UpdateScreen(); // 刷新屏幕显示 //} //void UpdateOLEDDisplay(void) //{ // char buf[32]; // // 直接使用 current_freq current_duty 显示 // sprintf(buf, "Freq: %lu Hz", (unsigned long)current_freq); // OLED_ShowString(0, 0, buf, 1); // 第一行显示频率 // sprintf(buf, "Duty: %d %%", current_duty); // OLED_ShowString(0, 8, buf, 1); // 第二行显示占空比 // // 绘制方波图形(x_start=0, y_center=32, height=16) // OLED_DrawSquareWave(0, 32, 16, (float)current_freq, (float)current_duty); // OLED_UpdateScreen(); // 统一刷新屏幕 //} void UpdateOLEDDisplay(void) { char buf[32]; sprintf(buf, "Freq: %lu Hz", (unsigned long)current_freq); OLED_ShowString(0, 0, buf, 1); // 显示频率 sprintf(buf, "Duty: %d %%", current_duty); OLED_ShowString(0, 8, buf, 1); // 显示占空比 OLED_UpdateText(); // 不再绘制静态波形图 // 所有波形由 OLED_UpdateWaveform() 负责动态刷新 } //重定向 int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart3, (uint8_t*)&ch, 1, HAL_MAX_DELAY); return ch; } int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart3, &ch, 1, 0xffff); 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_TIM2_Init(); MX_TIM1_Init(); MX_TIM3_Init(); MX_TIM4_Init(); MX_I2C1_Init(); MX_USART3_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ OLED_Init(); OLED_Clear(); // 初始化目标参数 OLED_SetWaveParams((float)current_freq, (float)current_duty); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); // 更新PWM HAL_UART_Receive_IT(&huart3, &rx_byte, 1);//启动中断 Update_ARR(); // 设置ARRCCR OLED_Fill(0); while (1) { Key_Scan_All(); // 每 50ms 更新一次文字显示 static uint32_t last_update = 0; if (HAL_GetTick() - last_update >= 50) { UpdateOLEDDisplay(); last_update = HAL_GetTick(); } // 动态刷新波形(约20Hz) OLED_UpdateWaveform(); //老式呼吸灯 // if (K1 == 0) // { // HAL_Delay(10); // if(K1 == 0) // { // while(K1==0); // n = -n; // // } // } // if (K2 == 0) //// { //// HAL_Delay(10); //// if(K2 == 0) //// { //// while(K2==0); //// z=f2; //// f2=f1; //// f1=f; //// f=z; //// } //// } if (!K1 && (HAL_GetTick() - key_last_time) > 10) { key_last_time = HAL_GetTick(); n = -n; while (!K1); // 等待释放(短时间可用) } if (!K2 && (HAL_GetTick() - key_last_time) > 10) { key_last_time = HAL_GetTick(); z=f2; f2=f1; f1=f; f=z; while (!K1); // 等待释放(短时间可用) } if (n == 1){ a=1;b=2;c=3;d=4; BreathingLight(); } else if(n == -1){ a=4;b=3;c=2;d=1; BreathingLight(); } // VOFA+ 数据发送(10Hz) static uint32_t last_vofa_send = 0; if (HAL_GetTick() - last_vofa_send >= 100) { Send_SquareWave_VOFA(); last_vofa_send = HAL_GetTick(); } HAL_Delay(1); // 防止死循环占用过高 // //1.阻塞式发送 // HAL_UART_Transmit(&huart3,(uint8_t *)"hello world",12,0XFFFF); // HAL_Delay(500); // //2.重定向 // printf("hello world"); // HAL_Delay(500); // //3.阻塞式接收 // uint8_t Buf[5]; // HAL_UART_Receive(&huart3,Buf,5,0XFFFF); // HAL_UART_Transmit(&huart3,Buf,5,0XFFFF); // HAL_Delay(500); /* 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}; /** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1; 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) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = 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 */ #include "key.h" #include "main.h" #include "tim.h" extern uint16_t current_freq; extern uint16_t current_duty; // 按键消抖函数 static uint8_t key_Debounce(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) // 假设低电平有效 { HAL_Delay(10); if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) { return 1; // 确认按下 } } return 0; } void Update_CCR(void) { uint32_t arr_val = __HAL_TIM_GET_AUTORELOAD(&htim1); // CCR = ARR * (Duty / 100.0) uint32_t CCR_new = (uint32_t)((float)(arr_val + 1) * (current_duty / 100.0f)) - 1; // 确保 CCR 不会超出 ARR if (CCR_new >= arr_val) CCR_new = arr_val - 1; if (CCR_new < 1) CCR_new = 1; // 使用 TIM1 Channel 2 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, CCR_new); } void Update_ARR(void) { uint32_t F_clk = HAL_RCC_GetPCLK2Freq(); uint32_t psc_val = htim1.Instance->PSC; // 保护除以零 if (current_freq == 0) current_freq = FREQ_MIN; uint32_t ARR_new = (F_clk / (current_freq * (psc_val + 1))) - 1; // 确保 ARR 不会溢出或过小 if (ARR_new > 0xFFFF) ARR_new = 0xFFFF; if (ARR_new < 1) ARR_new = 1; __HAL_TIM_DISABLE(&htim1); __HAL_TIM_SET_AUTORELOAD(&htim1, ARR_new); __HAL_TIM_ENABLE(&htim1); Update_CCR(); } void Key_Scan_All(void) { // PINA2: 频率增加 (F+) if (key_Debounce(GPIOA, GPIO_PIN_2) == 1) { if (current_freq < FREQ_MAX) { current_freq += FREQ_STEP; if (current_freq > FREQ_MAX) current_freq = FREQ_MAX; Update_ARR(); // 更新频率 CCR } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET); // 等待按键释放 } // PINA3: 频率减小 (F-) if (key_Debounce(GPIOA, GPIO_PIN_3) == 1) { if (current_freq > FREQ_MIN) { current_freq -= FREQ_STEP; if (current_freq < FREQ_MIN) current_freq = FREQ_MIN; Update_ARR(); // 更新频率 CCR } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_RESET); } // PINA4: 占空比增加 (D+) if (key_Debounce(GPIOA, GPIO_PIN_4) == 1) { if (current_duty < DUTY_MAX) { current_duty += DUTY_STEP; if (current_duty > DUTY_MAX) current_duty = DUTY_MAX; Update_CCR(); // 更新占空比 } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET); } // PINA5: 占空比减小 (D-) if (key_Debounce(GPIOA, GPIO_PIN_5) == 1) { if (current_duty > DUTY_MIN) { current_duty -= DUTY_STEP; if (current_duty < DUTY_MIN) current_duty = DUTY_MIN; Update_CCR(); // 更新占空比 } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET); // 等待按键释放 } } #include "i2c.h" // 包含hi2c1的声明 #include "sdd1306.h" #include "font6x8.h" #include <string.h> #define OLED_I2C_ADDR 0x78 // OLED I2C写地址 (0x3C<<1) #define OLED_WIDTH 128 #define OLED_HEIGHT 64 static uint8_t OLED_Buffer[OLED_WIDTH * OLED_HEIGHT / 8]; //static uint8_t wave_x = 0; // 波形当前的X坐标 static uint32_t last_draw_time = 0; // 上次绘制波形的时间戳 static uint8_t wave_color = 1; // 波形颜色 (1 = 白色) static float target_frequency = 1.0f; // 默认频率 1Hz static float target_duty_cycle = 50.0f; // 默认占空比 50% uint8_t OLED_GetPixel(int x, int y) { if (x < 0 || x >= OLED_WIDTH || y < 0 || y >= OLED_HEIGHT) return 0; uint8_t page = y / 8; uint8_t bit = y % 8; return (OLED_Buffer[page * OLED_WIDTH + x] >> bit) & 0x01; } void OLED_WriteCommand(uint8_t cmd) { // 发送一个命令字节 (控制字节=0x00) HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, HAL_MAX_DELAY); } void OLED_WriteData(uint8_t data) { // 发送一个数据字节 (控制字节=0x40) HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); } #include "font6x8.h" const unsigned char font6x8[][6] = { // ASCII 0x20 (空格) - 无像素 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, {0x00, 0x00, 0x5F, 0x00, 0x00, 0x00}, // ! {0x00, 0x07, 0x00, 0x07, 0x00, 0x00}, // " {0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00}, // # {0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00}, // $ {0x23, 0x13, 0x08, 0x64, 0x62, 0x00}, // % {0x36, 0x49, 0x55, 0x22, 0x50, 0x00}, // & {0x00, 0x05, 0x03, 0x00, 0x00, 0x00}, // ' {0x00, 0x1C, 0x22, 0x41, 0x00, 0x00}, // ( {0x00, 0x41, 0x22, 0x1C, 0x00, 0x00}, // ) {0x14, 0x08, 0x3E, 0x08, 0x14, 0x00}, // * {0x08, 0x08, 0x3E, 0x08, 0x08, 0x00}, // + {0x00, 0x50, 0x30, 0x00, 0x00, 0x00}, // , {0x08, 0x08, 0x08, 0x08, 0x08, 0x00}, // - {0x00, 0x60, 0x60, 0x00, 0x00, 0x00}, // . {0x20, 0x10, 0x08, 0x04, 0x02, 0x00}, // / {0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00}, // 0 {0x00, 0x42, 0x7F, 0x40, 0x00, 0x00}, // 1 {0x42, 0x61, 0x51, 0x49, 0x46, 0x00}, // 2 {0x21, 0x41, 0x45, 0x4B, 0x31, 0x00}, // 3 {0x18, 0x14, 0x12, 0x7F, 0x10, 0x00}, // 4 {0x27, 0x45, 0x45, 0x45, 0x39, 0x00}, // 5 {0x3C, 0x4A, 0x49, 0x49, 0x30, 0x00}, // 6 {0x01, 0x71, 0x09, 0x05, 0x03, 0x00}, // 7 {0x36, 0x49, 0x49, 0x49, 0x36, 0x00}, // 8 {0x06, 0x49, 0x49, 0x29, 0x1E, 0x00}, // 9 {0x00, 0x36, 0x36, 0x00, 0x00, 0x00}, // : {0x00, 0x56, 0x36, 0x00, 0x00, 0x00}, // ; {0x08, 0x14, 0x22, 0x41, 0x00, 0x00}, // < {0x14, 0x14, 0x14, 0x14, 0x14, 0x00}, // = {0x00, 0x41, 0x22, 0x14, 0x08, 0x00}, // > {0x02, 0x01, 0x51, 0x09, 0x06, 0x00}, // ? {0x32, 0x49, 0x59, 0x51, 0x3E, 0x00}, // @ {0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00}, // A {0x7F, 0x49, 0x49, 0x49, 0x36, 0x00}, // B {0x3E, 0x41, 0x41, 0x41, 0x22, 0x00}, // C {0x7F, 0x41, 0x41, 0x22, 0x1C, 0x00}, // D {0x7F, 0x49, 0x49, 0x49, 0x41, 0x00}, // E {0x7F, 0x09, 0x09, 0x09, 0x01, 0x00}, // F {0x3E, 0x41, 0x49, 0x49, 0x7A, 0x00}, // G {0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00}, // H {0x00, 0x41, 0x7F, 0x41, 0x00, 0x00}, // I {0x20, 0x40, 0x41, 0x3F, 0x01, 0x00}, // J {0x7F, 0x08, 0x14, 0x22, 0x41, 0x00}, // K {0x7F, 0x40, 0x40, 0x40, 0x40, 0x00}, // L {0x7F, 0x02, 0x0C, 0x02, 0x7F, 0x00}, // M {0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00}, // N {0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00}, // O {0x7F, 0x09, 0x09, 0x09, 0x06, 0x00}, // P {0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00}, // Q {0x7F, 0x09, 0x19, 0x29, 0x46, 0x00}, // R {0x46, 0x49, 0x49, 0x49, 0x31, 0x00}, // S {0x01, 0x01, 0x7F, 0x01, 0x01, 0x00}, // T {0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00}, // U {0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00}, // V {0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00}, // W {0x63, 0x14, 0x08, 0x14, 0x63, 0x00}, // X {0x07, 0x08, 0x70, 0x08, 0x07, 0x00}, // Y {0x61, 0x51, 0x49, 0x45, 0x43, 0x00}, // Z {0x00, 0x7F, 0x41, 0x41, 0x00, 0x00}, // [ {0x02, 0x04, 0x08, 0x10, 0x20, 0x00}, // backslash {0x00, 0x41, 0x41, 0x7F, 0x00, 0x00}, // ] {0x04, 0x02, 0x01, 0x02, 0x04, 0x00}, // ^ {0x80, 0x80, 0x80, 0x80, 0x80, 0x00}, // _ {0x00, 0x03, 0x07, 0x08, 0x00, 0x00}, // ` {0x20, 0x54, 0x54, 0x54, 0x78, 0x00}, // a {0x7F, 0x48, 0x44, 0x44, 0x38, 0x00}, // b {0x38, 0x44, 0x44, 0x44, 0x28, 0x00}, // c {0x38, 0x44, 0x44, 0x48, 0x7F, 0x00}, // d {0x38, 0x54, 0x54, 0x54, 0x18, 0x00}, // e {0x08, 0x7E, 0x09, 0x01, 0x02, 0x00}, // f {0x18, 0xA4, 0xA4, 0xA4, 0x7C, 0x00}, // g {0x7F, 0x08, 0x04, 0x04, 0x78, 0x00}, // h {0x00, 0x44, 0x7D, 0x40, 0x00, 0x00}, // i {0x40, 0x80, 0x84, 0x7D, 0x00, 0x00}, // j {0x7F, 0x10, 0x28, 0x44, 0x00, 0x00}, // k {0x00, 0x41, 0x7F, 0x40, 0x00, 0x00}, // l {0x7C, 0x04, 0x18, 0x04, 0x78, 0x00}, // m {0x7C, 0x04, 0x04, 0x04, 0x78, 0x00}, // n {0x38, 0x44, 0x44, 0x44, 0x38, 0x00}, // o {0xFC, 0x24, 0x24, 0x24, 0x18, 0x00}, // p {0x18, 0x24, 0x24, 0x24, 0xFC, 0x00}, // q {0x7C, 0x08, 0x04, 0x04, 0x08, 0x00}, // r {0x48, 0x54, 0x54, 0x54, 0x24, 0x00}, // s {0x04, 0x04, 0x3F, 0x44, 0x40, 0x00}, // t {0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00}, // u {0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00}, // v {0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00}, // w {0x44, 0x28, 0x10, 0x28, 0x44, 0x00}, // x {0x1C, 0xA0, 0xA0, 0xA0, 0x7C, 0x00}, // y {0x44, 0x64, 0x54, 0x4C, 0x44, 0x00}, // z {0x00, 0x08, 0x36, 0x41, 0x00, 0x00}, // { {0x00, 0x00, 0x77, 0x00, 0x00, 0x00}, // | {0x00, 0x41, 0x36, 0x08, 0x00, 0x00}, // } {0x02, 0x01, 0x02, 0x04, 0x02, 0x00} // ~ }; #include "vofa.h" #include <stdio.h> #include "main.h" #include "math.h" //void Send_SquareWave_VOFA(void) //{ // float t = HAL_GetTick() * 0.001f; // float freq = (float)current_freq; // float duty_cycle = (float)current_duty / 100.0f; // // 计算当前相位 // float phase = fmodf(t * freq, 1.0f); // float pwm_val = (phase < duty_cycle) ? 1.0f : 0.0f; // // 发送给VOFA+ // printf("%.3f\n", pwm_val); // 单通道模式下只需输出一个值 //} void Send_SquareWave_VOFA(void) { float t = HAL_GetTick() * 0.001f; // 当前时间(秒) float freq = (float)current_freq; // 频率(Hz) float duty_cycle = (float)current_duty / 100.0f; // 占空比(0~1) // 计算当前相位并生成 PWM 信号 float phase = fmodf(t * freq, 1.0f); float pwm_val = (phase < duty_cycle) ? 1.0f : 0.0f; // 同时发送 PWM 波形、频率、占空比(三通道) printf("<raw>%.3f,%.3f,%.3f\n", pwm_val, freq, duty_cycle); } 换成静态可调节方波 void OLED_UpdateScreen(void) { // 刷新OLED显示 for (uint8_t page = 0; page < 8; page++) { OLED_WriteCommand(0xB0 + page); // 设置页地址(0xB0 = page0) OLED_WriteCommand(0x00); // 设置列低4位为0 OLED_WriteCommand(0x10); // 设置列高4位为0 (列地址0) // 按当前页,将该页的缓冲区数据写出128字节 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_Clear(void) { memset(OLED_Buffer, 0x00, sizeof(OLED_Buffer)); // 缓冲设0 // 刷新OLED显示 for (uint8_t page = 0; page < 8; page++) { OLED_WriteCommand(0xB0 + page); // 设置页地址(0xB0 = page0) OLED_WriteCommand(0x00); // 设置列低4位为0 OLED_WriteCommand(0x10); // 设置列高4位为0 (列地址0) // 按当前页,将该页的缓冲区数据写出128字节 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_Fill(uint8_t color) { // color=1 -> 全亮; color=0 -> 全黑 uint8_t fill = (color ? 0xFF : 0x00); memset(OLED_Buffer, fill, sizeof(OLED_Buffer)); // 刷新显示(与OLED_Clear类似) for (uint8_t page = 0; page < 8; page++) { OLED_WriteCommand(0xB0 + page); OLED_WriteCommand(0x00); OLED_WriteCommand(0x10); HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_Init(void) { HAL_Delay(100); // 上电延时,确保OLED电源稳定 OLED_WriteCommand(0xAE); // 1. 显示关闭 OLED_WriteCommand(0xD5); // 2. 设置显示时钟分频/振荡频率 OLED_WriteCommand(0x80); // 分频因子&振荡频率设置,0x80默认 OLED_WriteCommand(0xA8); // 3. 设置多路复用比 OLED_WriteCommand(0x3F); // 64行 (0x3F) OLED_WriteCommand(0xD3); // 4. 设置显示偏移 OLED_WriteCommand(0x00); // 无偏移 OLED_WriteCommand(0x40); // 5. 设置显示开始行 (0) OLED_WriteCommand(0x8D); // 6. 电荷泵设置 OLED_WriteCommand(0x14); // 开启电荷泵 OLED_WriteCommand(0x20); // 7. 内存地址模式 OLED_WriteCommand(0x02); // 页地址模式 (Page Mode) OLED_WriteCommand(0xA1); // 8. 列地址重映射 (A0->A1 左右翻转) OLED_WriteCommand(0xC8); // 9. 行扫描方向翻转 (上下翻转) OLED_WriteCommand(0xDA); // 10. COM 引脚配置 OLED_WriteCommand(0x12); // COM配置 (0x12 for 64行) OLED_WriteCommand(0x81); // 11. 对比度设置 OLED_WriteCommand(0x7F); // 对比度值 (0x7F) OLED_WriteCommand(0xD9); // 12. 预充电周期 OLED_WriteCommand(0xF1); // 设置为 0xF1 OLED_WriteCommand(0xDB); // 13. 设置 VCOMH 电平 OLED_WriteCommand(0x40); // 0x40 默认 OLED_WriteCommand(0xA4); // 14. 取消全显示,按照RAM内容显示 OLED_WriteCommand(0xA6); // 15. 正常显示 (非反相) OLED_WriteCommand(0xAF); // 16. 开启显示 } //OLED_DrawPixel(x, y, color) : 绘制单个像素到缓冲区 void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) { if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return; // 越界保护 uint16_t index = (y / 8) * OLED_WIDTH + x; uint8_t bit = y % 8; if (color) OLED_Buffer[index] |= (1 << bit); // 置位 else OLED_Buffer[index] &= ~(1 << bit); // 清位 } void OLED_DrawChar(uint8_t x, uint8_t y, char chr, uint8_t color) { if (chr < 0x20 || chr > 0x7E) chr = '?'; // 非可显示字符用 '?' 代替 uint8_t index = chr - 0x20; // 每个字符宽6 for (uint8_t col = 0; col < 6; col++) { uint8_t lineBits = font6x8[index][col]; // lineBits的每个位对应该列的像素,从字模取出 for (uint8_t bit = 0; bit < 8; bit++) { uint8_t pixelColor = (lineBits >> bit) & 0x1; // 取该位 if (!color) pixelColor = !pixelColor; // 如果color=0黑底白字,可取反 OLED_DrawPixel(x + col, y + bit, pixelColor); } } } //OLED_ShowString(x, y, *str, font, color) : 从指定位置开始显示字符串。 void OLED_ShowString(uint8_t x, uint8_t y, const char *str, uint8_t color) { while (*str) { OLED_DrawChar(x, y, *str, color); x += 6; // 移动光标6列(字体宽度) if (x + 6 > OLED_WIDTH) { x = 0; // 换行到头 y += 8; // 下移一页(8像素高) } if (y >= OLED_HEIGHT) { break; // 超出屏幕则退出 } str++; } } //画线 void OLED_DrawLine(int x0, int y0, int x1, int y1, uint8_t color) { if (x0 == x1 && y0 == y1) { OLED_DrawPixel(x0, y0, color); return; } int dx = (x1 > x0 ? x1 - x0 : x0 - x1); int dy = (y1 > y0 ? y0 - y1 : y1 - y0); int sx = x0 < x1 ? 1 : -1; int sy = y0 < y1 ? 1 : -1; int err = dx + dy; // 注意dy已取负 while (1) { OLED_DrawPixel(x0, y0, color); if (x0 == x1 && y0 == y1) break; int e2 = 2 * err; if (e2 >= dy) { err += dy; x0 += sx; } if (e2 <= dx) { err += dx; y0 += sy; } } } //画矩形 void OLED_DrawRectangle(int x, int y, int width, int height, uint8_t color) { // 绘制矩形的四条边 OLED_DrawLine(x, y, x + width - 1, y, color); // 上边 OLED_DrawLine(x, y + height - 1, x + width - 1, y + height - 1, color); // 下边 OLED_DrawLine(x, y, x, y + height - 1, color); // 左边 OLED_DrawLine(x + width - 1, y, x + width - 1, y + height - 1, color); // 右边 } //画圆 void OLED_DrawCircle(int x0, int y0, int r, uint8_t color) { int a = 0; int b = r; int d = 1 - r; // 判定参数 while (a <= b) { // 八个对称点 OLED_DrawPixel(x0 + a, y0 + b, color); OLED_DrawPixel(x0 - a, y0 + b, color); OLED_DrawPixel(x0 + a, y0 - b, color); OLED_DrawPixel(x0 - a, y0 - b, color); OLED_DrawPixel(x0 + b, y0 + a, color); OLED_DrawPixel(x0 - b, y0 + a, color); OLED_DrawPixel(x0 + b, y0 - a, color); OLED_DrawPixel(x0 - b, y0 - a, color); a++; if (d < 0) { d += 2 * a + 1; } else { b--; d += 2 * (a - b) + 1; } } } //void OLED_DrawSquareWave(int x_start, int y_center, int height, float frequency, float duty_cycle) //{ // for(int page = 4; page < 8; page++) // { // memset(&OLED_Buffer[page * OLED_WIDTH], 0x00, OLED_WIDTH); // } // if (frequency <= 0.0f) return; // if (duty_cycle <= 0.0f) duty_cycle = 0.0f; // 边界保护 // if (duty_cycle >= 100.0f) duty_cycle = 100.0f; // 边界保护 // int y_high = y_center - height / 2; // 高电平 Y 坐标 // int y_low = y_center + height / 2; // 低电平 Y 坐标 // int x_end = OLED_WIDTH; // 绘制到屏幕右侧 // int width = x_end - x_start; // 绘制区域总宽度 // int num_cycles = 2; // // int cycle_width = width / num_cycles; // int high_width = (int)((float)cycle_width * (duty_cycle / 100.0f)); // int low_width = cycle_width - high_width; // // if (duty_cycle > 0.0f && high_width == 0) high_width = 1; // if (duty_cycle < 100.0f && low_width == 0) low_width = 1; // // int current_x = x_start; // for (int cycle = 0; cycle < num_cycles; cycle++) // { // int x_start_cycle = current_x; // // OLED_DrawLine(current_x, y_high, current_x + high_width - 1, y_high, wave_color); // current_x += high_width; // // if (duty_cycle > 0.0f && duty_cycle < 100.0f) { // OLED_DrawLine(current_x - 1, y_high, current_x - 1, y_low, wave_color); // } // OLED_DrawLine(current_x, y_low, current_x + low_width - 1, y_low, wave_color); // current_x += low_width; // // if (duty_cycle > 0.0f && duty_cycle < 100.0f) { // OLED_DrawLine(current_x - 1, y_low, current_x - 1, y_high, wave_color); // } // // if (current_x >= x_end) break; // } //} //void OLED_UpdateWaveform(void) //{ // static uint32_t last_draw_time = 0; // uint32_t now = HAL_GetTick(); // // 控制刷新率:~50Hz // if (now - last_draw_time < 20) return; // last_draw_time = now; // float t = now / 1000.0f; // 时间(秒) // float freq = (float)current_freq; // float duty = (float)current_duty / 100.0f; // // 计算当前应显示的电平状态 // float phase = fmodf(t * freq, 1.0f); // uint8_t is_high = (phase < duty) ? 1 : 0; // // 波形显示区域(垂直方向) // const int y_center = 48; // const int height = 16; // const int y_top = y_center - height / 2; // y=40 // const int y_bottom = y_center + height / 2; // y=56 // // === 步骤1:整体左移一列(关键!避免“方块”)=== // for (int y = y_top; y <= y_bottom; y++) // { // for (int x = 0; x < OLED_WIDTH - 1; x++) // { // uint8_t pixel = OLED_GetPixel(x + 1, y); // OLED_DrawPixel(x, y, pixel); // 把右边的像素移到左边 // } // } // // === 步骤2:在最右侧绘制新点(当前时刻电平)=== // for (int y = y_top; y <= y_bottom; y++) // { // OLED_DrawPixel(OLED_WIDTH - 1, y, is_high ? 1 : 0); // } // // === 步骤3:仅刷新受影响的页面(page 5~7 对应 y=40~63)=== // for (uint8_t page = 5; page <= 7; page++) // { // OLED_WriteCommand(0xB0 + page); // 设置页地址 // OLED_WriteCommand(0x00); // 低列地址 // OLED_WriteCommand(0x10); // 高列地址 // HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, // I2C_MEMADD_SIZE_8BIT, // &OLED_Buffer[page * OLED_WIDTH], // OLED_WIDTH, HAL_MAX_DELAY); // } //} // void OLED_SetWaveParams(float freq, float duty_cycle) { target_frequency = freq; target_duty_cycle = duty_cycle; } void OLED_UpdateText(void) { for (uint8_t page = 0; page < 4; page++) // page 0~3 对应 y=0~31 { OLED_WriteCommand(0xB0 + page); // 设置页地址 OLED_WriteCommand(0x00); // 低列地址 OLED_WriteCommand(0x10); // 高列地址 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_UpdateWaveform(void) { static uint32_t last_draw_time = 0; uint32_t now = HAL_GetTick(); // 控制刷新率为 10 FPS(每 100ms 一帧) if (now - last_draw_time < 100) return; last_draw_time = now; const int y_center = 48; const int height = 16; const int y_top = y_center - height / 2; // y=40 const int y_bottom = y_center + height / 2; // y=56 float total_duration = 12.8f; // 总时间跨度 12.8 秒 float dt_per_pixel = total_duration / 128.0f; // 每列 ≈0.1秒 for (int x = 0; x < OLED_WIDTH; x++) { // 计算这一列对应的时间点(从现在往回推) float t_back = now / 1000.0f - (OLED_WIDTH - 1 - x) * dt_per_pixel; float phase = fmodf(t_back * current_freq, 1.0f); uint8_t is_high = (phase < (current_duty / 100.0f)) ? 1 : 0; // 绘制该列:高电平区域点亮 for (int y = y_top; y <= y_bottom; y++) { OLED_DrawPixel(x, y, is_high); } } // === 只刷新受影响的页面(page 5~7)=== for (uint8_t page = 5; page <= 7; page++) { OLED_WriteCommand(0xB0 + page); // 设置页地址 OLED_WriteCommand(0x00); // 列低4位 OLED_WriteCommand(0x10); // 列高4位 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } }
11-27
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 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 "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "math.h" #include "font6x8.h" #include "sdd1306.h" #include "key.h" #include "stdio.h" #include "vofa.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ int a,b,c,d; float f=0.5;float f1=1;float f2=2;float z=0; float t=3.14/4; int n =1; uint16_t current_freq = 100; uint16_t current_duty = 50; #define K1 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11) #define K2 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12) #define BUFFER_SIZE 64 uint8_t rx_byte; // 当前接收到的字节 char rx_buffer[BUFFER_SIZE]; // 接收缓冲区 uint16_t rx_index = 0; // 当前写入位置 /* 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 */ void ProcessReceivedString(char *input); static uint32_t key_last_time = 0; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//中断回调 { if (huart == &huart3) { // 如果收到的是 '\n' 或 '\r',表示命令结束 if (rx_byte == '\n' || rx_byte == '\r') { if (rx_index > 0) { rx_buffer[rx_index] = '\0'; // 结束字符串 // 处理接收到的内容 ProcessReceivedString(rx_buffer); // 清空缓冲区 rx_index = 0; } } else { // 普通字符加入缓冲区(防溢出) if (rx_index < BUFFER_SIZE - 1) { rx_buffer[rx_index++] = rx_byte; } } // 重新开启下一次接收 HAL_UART_Receive_IT(&huart3, &rx_byte, 1); } } void ProcessReceivedString(char *input) { char output[128]; // 构造输出:"xx is ok !" snprintf(output, sizeof(output), "%s is ok !", input); // 发送回上位机 printf("Command received: %s\r\n", input); // 如果有 OLED 屏幕,也在屏幕上显示 OLED_Clear(); OLED_ShowString(0, 16, output,1); OLED_UpdateScreen(); } static void BreathingLight(void){ float t1 = HAL_GetTick()*0.001; float duty1 = 0.5 * sin(f*3.14*t1+a*t/f)+0.5; uint16_t arr1 = __HAL_TIM_GET_AUTORELOAD(&htim1); uint16_t ccr1 = duty1 * (arr1+1); __HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,ccr1); float t2 = HAL_GetTick()*0.001; float duty2 = 0.5 * sin(f*3.14*t2+b*t/f)+0.5; uint16_t arr2 = __HAL_TIM_GET_AUTORELOAD(&htim2); uint16_t ccr2 = duty2* (arr2+1); __HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,ccr2); float t3 = HAL_GetTick()*0.001; float duty3 = 0.5 * sin(f*3.14*t3+c*t/f)+0.5; uint16_t arr3 = __HAL_TIM_GET_AUTORELOAD(&htim3); uint16_t ccr3 = duty3 * (arr3+1); __HAL_TIM_SET_COMPARE(&htim3,TIM_CHANNEL_1,ccr3); float t4 = HAL_GetTick()*0.001; float duty4 = 0.5 * sin(f*3.14*t4+d*t/f)+0.5; uint16_t arr4 = __HAL_TIM_GET_AUTORELOAD(&htim4); uint16_t ccr4 = duty4 * (arr4+1); __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_3,ccr4); } //static void UpdateOLEDDisplay(void) //{ // uint32_t psc = htim1.Init.Prescaler; // uint32_t arr = htim1.Init.Period; // // float timer_clock_freq = (float)HAL_RCC_GetPCLK2Freq(); // float freq = timer_clock_freq / ((arr + 1) * (psc + 1)); // // // 3. 获取TIM1通道1的比较值(CCR) // uint32_t ccr = __HAL_TIM_GET_COMPARE(&htim1, TIM_CHANNEL_2); // // // 4. 计算占空比 // float duty_cycle = (float)ccr / (arr + 1) * 100.0f; // 转换为百分比 // char display_buf[32]; // // // 3. 将数值格式化为字符串 // sprintf(display_buf, "Freq: %.1f Hz", freq); // OLED_ShowString(0, 0, (uint8_t*)display_buf, 16); // // // sprintf(display_buf, "Duty: %.1f %%", duty_cycle); // OLED_ShowString(0, 8, (uint8_t*)display_buf, 16); // // // OLED_DrawSquareWave(0, 32, 16, freq, duty_cycle); // // OLED_UpdateScreen(); // 刷新屏幕显示 //} void UpdateOLEDDisplay(void) { char buf[32]; // 直接使用 current_freq current_duty 显示 sprintf(buf, "Freq: %lu Hz", (unsigned long)current_freq); OLED_ShowString(0, 0, buf, 1); // 第一行显示频率 sprintf(buf, "Duty: %d %%", current_duty); OLED_ShowString(0, 8, buf, 1); // 第二行显示占空比 // 绘制方波图形(x_start=0, y_center=32, height=16) OLED_DrawSquareWave(0, 48, 16, (float)current_freq, (float)current_duty); OLED_UpdateScreen(); // 统一刷新屏幕 } //void UpdateOLEDDisplay(void) //{ // char buf[32]; // sprintf(buf, "Freq: %lu Hz", (unsigned long)current_freq); // OLED_ShowString(0, 0, buf, 1); // 显示频率 // sprintf(buf, "Duty: %d %%", current_duty); // OLED_ShowString(0, 8, buf, 1); // 显示占空比 // // OLED_UpdateText(); // // 不再绘制静态波形图 // // 所有波形由 OLED_UpdateWaveform() 负责动态刷新 //} //void UpdateOLEDDisplay(void) //{ // char buf[32]; // // 更新文字区 // sprintf(buf, "Freq: %lu Hz", (unsigned long)current_freq); // OLED_ShowString(0, 0, buf, 1); // 第一行 // sprintf(buf, "Duty: %d %%", current_duty); // OLED_ShowString(0, 8, buf, 1); // 第二行 // // === 绘制静态方波图 === // OLED_DrawStaticSquareWave(0, 48, 16, (float)current_freq, (float)current_duty); // // === 刷新屏幕指定区域 === // OLED_UpdateText(); // 刷新上半屏(文字) // OLED_UpdateWaveformArea(); // 刷新下半屏波形(自定义函数) //} //重定向 int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart3, (uint8_t*)&ch, 1, HAL_MAX_DELAY); return ch; } int fgetc(FILE *f) { uint8_t ch = 0; HAL_UART_Receive(&huart3, &ch, 1, 0xffff); 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_TIM2_Init(); MX_TIM1_Init(); MX_TIM3_Init(); MX_TIM4_Init(); MX_I2C1_Init(); MX_USART3_UART_Init(); /* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim4,TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ OLED_Init(); OLED_Clear(); // 初始化目标参数 OLED_SetWaveParams((float)current_freq, (float)current_duty); HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); // 更新PWM HAL_UART_Receive_IT(&huart3, &rx_byte, 1);//启动中断 current_freq = 1000; // 固定为 1kHz,不再变化 Update_ARR(); // 设置ARRCCR OLED_Fill(0); while (1) { Key_Scan_All(); // static uint32_t last_update = 0; // if (HAL_GetTick() - last_update >= 100) // 每100ms刷新一次显示 // { // UpdateOLEDDisplay(); // last_update = HAL_GetTick(); // // } // // 每 50ms 更新一次文字显示 // static uint32_t last_update = 0; // if (HAL_GetTick() - last_update >= 50) // { // UpdateOLEDDisplay(); // last_update = HAL_GetTick(); // } // // 动态刷新波形(约20Hz) // OLED_UpdateWaveform(); //老式呼吸灯 // if (K1 == 0) // { // HAL_Delay(10); // if(K1 == 0) // { // while(K1==0); // n = -n; // // } // } // if (K2 == 0) //// { //// HAL_Delay(10); //// if(K2 == 0) //// { //// while(K2==0); //// z=f2; //// f2=f1; //// f1=f; //// f=z; //// } //// } if (!K1 && (HAL_GetTick() - key_last_time) > 10) { key_last_time = HAL_GetTick(); n = -n; while (!K1); // 等待释放(短时间可用) } if (!K2 && (HAL_GetTick() - key_last_time) > 10) { key_last_time = HAL_GetTick(); z=f2; f2=f1; f1=f; f=z; while (!K1); // 等待释放(短时间可用) } if (n == 1){ a=1;b=2;c=3;d=4; BreathingLight(); } else if(n == -1){ a=4;b=3;c=2;d=1; BreathingLight(); } // VOFA+ 数据发送(10Hz) static uint32_t last_vofa_send = 0; if (HAL_GetTick() - last_vofa_send >= 100) { Send_SquareWave_VOFA(); last_vofa_send = HAL_GetTick(); } HAL_Delay(10); // 防止死循环占用过高 // //1.阻塞式发送 // HAL_UART_Transmit(&huart3,(uint8_t *)"hello world",12,0XFFFF); // HAL_Delay(500); // //2.重定向 // printf("hello world"); // HAL_Delay(500); // //3.阻塞式接收 // uint8_t Buf[5]; // HAL_UART_Receive(&huart3,Buf,5,0XFFFF); // HAL_UART_Transmit(&huart3,Buf,5,0XFFFF); // HAL_Delay(500); /* 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}; /** 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.HSEPredivValue = RCC_HSE_PREDIV_DIV1; 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) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = 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 */#include "i2c.h" // 包含hi2c1的声明 #include "sdd1306.h" #include "font6x8.h" #include <string.h> #define OLED_I2C_ADDR 0x78 // OLED I2C写地址 (0x3C<<1) #define OLED_WIDTH 128 #define OLED_HEIGHT 64 static uint8_t OLED_Buffer[OLED_WIDTH * OLED_HEIGHT / 8]; #define WAVE_WIDTH 128 #define WAVE_Y_CENTER 48 #define WAVE_HEIGHT 16 // 全局变量 uint8_t wave_data[WAVE_WIDTH] = {0}; // 当前波形数据(0=低,非0=高) float duty_cycle = 50.0f; // 初始占空比 50% uint32_t last_update_ms = 0; const float frequency = 1000.0f; // 固定频率 1kHz //static uint8_t wave_x = 0; // 波形当前的X坐标 static uint32_t last_draw_time = 0; // 上次绘制波形的时间戳 static uint8_t wave_color = 1; // 波形颜色 (1 = 白色) static float target_frequency = 1.0f; // 默认频率 1Hz static float target_duty_cycle = 50.0f; // 默认占空比 50% uint8_t OLED_GetPixel(int x, int y) { if (x < 0 || x >= OLED_WIDTH || y < 0 || y >= OLED_HEIGHT) return 0; uint8_t page = y / 8; uint8_t bit = y % 8; return (OLED_Buffer[page * OLED_WIDTH + x] >> bit) & 0x01; } void OLED_UpdateWaveformArea(void) { for (uint8_t page = 0; page <= 2; page++) // 后三页就够了 { OLED_WriteCommand(0xB0 + page); // 设置页地址 OLED_WriteCommand(0x00); // 列地址低4位 = 0 OLED_WriteCommand(0x10); // 列地址高4位 = 0 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, // 数据模式 I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * 128], // 缓冲区偏移 128, // 发送128列数据 HAL_MAX_DELAY); } } void OLED_WriteCommand(uint8_t cmd) { // 发送一个命令字节 (控制字节=0x00) HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, &cmd, 1, HAL_MAX_DELAY); } void OLED_WriteData(uint8_t data) { // 发送一个数据字节 (控制字节=0x40) HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &data, 1, HAL_MAX_DELAY); } void OLED_UpdateScreen(void) { // 刷新OLED显示 for (uint8_t page = 0; page < 8; page++) { OLED_WriteCommand(0xB0 + page); // 设置页地址(0xB0 = page0) OLED_WriteCommand(0x00); // 设置列低4位为0 OLED_WriteCommand(0x10); // 设置列高4位为0 (列地址0) // 按当前页,将该页的缓冲区数据写出128字节 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_Clear(void) { memset(OLED_Buffer, 0x00, sizeof(OLED_Buffer)); // 缓冲设0 // 刷新OLED显示 for (uint8_t page = 0; page < 8; page++) { OLED_WriteCommand(0xB0 + page); // 设置页地址(0xB0 = page0) OLED_WriteCommand(0x00); // 设置列低4位为0 OLED_WriteCommand(0x10); // 设置列高4位为0 (列地址0) // 按当前页,将该页的缓冲区数据写出128字节 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_Fill(uint8_t color) { // color=1 -> 全亮; color=0 -> 全黑 uint8_t fill = (color ? 0xFF : 0x00); memset(OLED_Buffer, fill, sizeof(OLED_Buffer)); // 刷新显示(与OLED_Clear类似) for (uint8_t page = 0; page < 8; page++) { OLED_WriteCommand(0xB0 + page); OLED_WriteCommand(0x00); OLED_WriteCommand(0x10); HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } void OLED_Init(void) { HAL_Delay(100); // 上电延时,确保OLED电源稳定 OLED_WriteCommand(0xAE); // 1. 显示关闭 OLED_WriteCommand(0xD5); // 2. 设置显示时钟分频/振荡频率 OLED_WriteCommand(0x80); // 分频因子&振荡频率设置,0x80默认 OLED_WriteCommand(0xA8); // 3. 设置多路复用比 OLED_WriteCommand(0x3F); // 64行 (0x3F) OLED_WriteCommand(0xD3); // 4. 设置显示偏移 OLED_WriteCommand(0x00); // 无偏移 OLED_WriteCommand(0x40); // 5. 设置显示开始行 (0) OLED_WriteCommand(0x8D); // 6. 电荷泵设置 OLED_WriteCommand(0x14); // 开启电荷泵 OLED_WriteCommand(0x20); // 7. 内存地址模式 OLED_WriteCommand(0x02); // 页地址模式 (Page Mode) OLED_WriteCommand(0xA1); // 8. 列地址重映射 (A0->A1 左右翻转) OLED_WriteCommand(0xC8); // 9. 行扫描方向翻转 (上下翻转) OLED_WriteCommand(0xDA); // 10. COM 引脚配置 OLED_WriteCommand(0x12); // COM配置 (0x12 for 64行) OLED_WriteCommand(0x81); // 11. 对比度设置 OLED_WriteCommand(0x7F); // 对比度值 (0x7F) OLED_WriteCommand(0xD9); // 12. 预充电周期 OLED_WriteCommand(0xF1); // 设置为 0xF1 OLED_WriteCommand(0xDB); // 13. 设置 VCOMH 电平 OLED_WriteCommand(0x40); // 0x40 默认 OLED_WriteCommand(0xA4); // 14. 取消全显示,按照RAM内容显示 OLED_WriteCommand(0xA6); // 15. 正常显示 (非反相) OLED_WriteCommand(0xAF); // 16. 开启显示 } //OLED_DrawPixel(x, y, color) : 绘制单个像素到缓冲区 void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color) { if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return; // 越界保护 uint16_t index = (y / 8) * OLED_WIDTH + x; uint8_t bit = y % 8; if (color) OLED_Buffer[index] |= (1 << bit); // 置位 else OLED_Buffer[index] &= ~(1 << bit); // 清位 } void OLED_DrawChar(uint8_t x, uint8_t y, char chr, uint8_t color) { if (chr < 0x20 || chr > 0x7E) chr = '?'; // 非可显示字符用 '?' 代替 uint8_t index = chr - 0x20; // 每个字符宽6 for (uint8_t col = 0; col < 6; col++) { uint8_t lineBits = font6x8[index][col]; // lineBits的每个位对应该列的像素,从字模取出 for (uint8_t bit = 0; bit < 8; bit++) { uint8_t pixelColor = (lineBits >> bit) & 0x1; // 取该位 if (!color) pixelColor = !pixelColor; // 如果color=0黑底白字,可取反 OLED_DrawPixel(x + col, y + bit, pixelColor); } } } //OLED_ShowString(x, y, *str, font, color) : 从指定位置开始显示字符串。 void OLED_ShowString(uint8_t x, uint8_t y, const char *str, uint8_t color) { while (*str) { OLED_DrawChar(x, y, *str, color); x += 6; // 移动光标6列(字体宽度) if (x + 6 > OLED_WIDTH) { x = 0; // 换行到头 y += 8; // 下移一页(8像素高) } if (y >= OLED_HEIGHT) { break; // 超出屏幕则退出 } str++; } } //画线 void OLED_DrawLine(int x0, int y0, int x1, int y1, uint8_t color) { if (x0 == x1 && y0 == y1) { OLED_DrawPixel(x0, y0, color); return; } int dx = (x1 > x0 ? x1 - x0 : x0 - x1); int dy = (y1 > y0 ? y0 - y1 : y1 - y0); int sx = x0 < x1 ? 1 : -1; int sy = y0 < y1 ? 1 : -1; int err = dx + dy; // 注意dy已取负 while (1) { OLED_DrawPixel(x0, y0, color); if (x0 == x1 && y0 == y1) break; int e2 = 2 * err; if (e2 >= dy) { err += dy; x0 += sx; } if (e2 <= dx) { err += dx; y0 += sy; } } } //画矩形 void OLED_DrawRectangle(int x, int y, int width, int height, uint8_t color) { // 绘制矩形的四条边 OLED_DrawLine(x, y, x + width - 1, y, color); // 上边 OLED_DrawLine(x, y + height - 1, x + width - 1, y + height - 1, color); // 下边 OLED_DrawLine(x, y, x, y + height - 1, color); // 左边 OLED_DrawLine(x + width - 1, y, x + width - 1, y + height - 1, color); // 右边 } //画圆 void OLED_DrawCircle(int x0, int y0, int r, uint8_t color) { int a = 0; int b = r; int d = 1 - r; // 判定参数 while (a <= b) { // 八个对称点 OLED_DrawPixel(x0 + a, y0 + b, color); OLED_DrawPixel(x0 - a, y0 + b, color); OLED_DrawPixel(x0 + a, y0 - b, color); OLED_DrawPixel(x0 - a, y0 - b, color); OLED_DrawPixel(x0 + b, y0 + a, color); OLED_DrawPixel(x0 - b, y0 + a, color); OLED_DrawPixel(x0 + b, y0 - a, color); OLED_DrawPixel(x0 - b, y0 - a, color); a++; if (d < 0) { d += 2 * a + 1; } else { b--; d += 2 * (a - b) + 1; } } } void InitWaveform(void) { for (int i = 0; i < WAVE_WIDTH; i++) { float t = i / 128.0f; // 归一化位置 float phase = t * 360.0f; if (phase < duty_cycle) { wave_data[i] = 1; } else { wave_data[i] = 0; } } } void UpdateScrollingWave(void) { uint32_t now = HAL_GetTick(); if (now - last_update_ms >= 20) // 每 20ms 更新一次(滚动速度) { // 左移所有数据 for (int i = 0; i < WAVE_WIDTH - 1; i++) { wave_data[i] = wave_data[i + 1]; } // 计算当前相位(基于固定频率) static float time_phase = 0.0f; float period_ms = 1000.0f / frequency; // 周期(ms) time_phase += 20.0f / period_ms; // 每次前进 20ms 的相位比例 if (time_phase >= 1.0f) time_phase -= 1.0f; // 插入新点(右边) wave_data[WAVE_WIDTH - 1] = (time_phase < (duty_cycle / 100.0f)) ? 1 : 0; last_update_ms = now; } } void DrawScrollingSquareWave(void) { // 清除旧波形区域(page 5~7) OLED_UpdateWaveformArea(); int y_center = WAVE_Y_CENTER; int h = WAVE_HEIGHT; for (int x = 0; x < WAVE_WIDTH; x++) { int y = wave_data[x] ? (y_center - h/2) : (y_center + h/2); OLED_DrawLine(x, y_center - h/2, x, y_center + h/2, 1); // 垂直线表示高低 } // 添加顶部底部连线形成完整方波 for (int x = 1; x < WAVE_WIDTH; x++) { if (wave_data[x] == wave_data[x-1]) { OLED_DrawLine(x-1, y_center - h/2, x, y_center - h/2, 1); // 上边 OLED_DrawLine(x-1, y_center + h/2, x, y_center + h/2, 1); // 下边 } else { OLED_DrawLine(x, y_center - h/2, x, y_center + h/2, 1); // 跳变沿 } } } void OLED_DrawSquareWave(int x_start, int y_center, int height, float frequency, float duty_cycle) { for(int page = 4; page < 8; page++) { memset(&OLED_Buffer[page * OLED_WIDTH], 0x00, OLED_WIDTH); } if (frequency <= 0.0f) return; if (duty_cycle <= 0.0f) duty_cycle = 0.0f; // 边界保护 if (duty_cycle >= 100.0f) duty_cycle = 100.0f; // 边界保护 int y_high = y_center - height / 2; // 高电平 Y 坐标 int y_low = y_center + height / 2; // 低电平 Y 坐标 int x_end = OLED_WIDTH; // 绘制到屏幕右侧 int width = x_end - x_start; // 绘制区域总宽度 int num_cycles = 2; int cycle_width = width / num_cycles; int high_width = (int)((float)cycle_width * (duty_cycle / 100.0f)); int low_width = cycle_width - high_width; if (duty_cycle > 0.0f && high_width == 0) high_width = 1; if (duty_cycle < 100.0f && low_width == 0) low_width = 1; int current_x = x_start; for (int cycle = 0; cycle < num_cycles; cycle++) { int x_start_cycle = current_x; OLED_DrawLine(current_x, y_high, current_x + high_width - 1, y_high, wave_color); current_x += high_width; if (duty_cycle > 0.0f && duty_cycle < 100.0f) { OLED_DrawLine(current_x - 1, y_high, current_x - 1, y_low, wave_color); } OLED_DrawLine(current_x, y_low, current_x + low_width - 1, y_low, wave_color); current_x += low_width; if (duty_cycle > 0.0f && duty_cycle < 100.0f) { OLED_DrawLine(current_x - 1, y_low, current_x - 1, y_high, wave_color); } if (current_x >= x_end) break; } } //void OLED_UpdateWaveform(void) //{ // static uint32_t last_draw_time = 0; // uint32_t now = HAL_GetTick(); // // 控制刷新率:~50Hz // if (now - last_draw_time < 20) return; // last_draw_time = now; // float t = now / 1000.0f; // 时间(秒) // float freq = (float)current_freq; // float duty = (float)current_duty / 100.0f; // // 计算当前应显示的电平状态 // float phase = fmodf(t * freq, 1.0f); // uint8_t is_high = (phase < duty) ? 1 : 0; // // 波形显示区域(垂直方向) // const int y_center = 48; // const int height = 16; // const int y_top = y_center - height / 2; // y=40 // const int y_bottom = y_center + height / 2; // y=56 // // === 步骤1:整体左移一列(关键!避免“方块”)=== // for (int y = y_top; y <= y_bottom; y++) // { // for (int x = 0; x < OLED_WIDTH - 1; x++) // { // uint8_t pixel = OLED_GetPixel(x + 1, y); // OLED_DrawPixel(x, y, pixel); // 把右边的像素移到左边 // } // } // // === 步骤2:在最右侧绘制新点(当前时刻电平)=== // for (int y = y_top; y <= y_bottom; y++) // { // OLED_DrawPixel(OLED_WIDTH - 1, y, is_high ? 1 : 0); // } // // === 步骤3:仅刷新受影响的页面(page 5~7 对应 y=40~63)=== // for (uint8_t page = 5; page <= 7; page++) // { // OLED_WriteCommand(0xB0 + page); // 设置页地址 // OLED_WriteCommand(0x00); // 低列地址 // OLED_WriteCommand(0x10); // 高列地址 // HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, // I2C_MEMADD_SIZE_8BIT, // &OLED_Buffer[page * OLED_WIDTH], // OLED_WIDTH, HAL_MAX_DELAY); // } //} // void OLED_SetWaveParams(float freq, float duty_cycle) { target_frequency = freq; target_duty_cycle = duty_cycle; } void OLED_UpdateText(void) { for (uint8_t page = 0; page < 4; page++) // page 0~3 对应 y=0~31 { OLED_WriteCommand(0xB0 + page); // 设置页地址 OLED_WriteCommand(0x00); // 低列地址 OLED_WriteCommand(0x10); // 高列地址 HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, &OLED_Buffer[page * OLED_WIDTH], OLED_WIDTH, HAL_MAX_DELAY); } } //void OLED_UpdateWaveform(void) //{ // static uint32_t last_draw_time = 0; // uint32_t now = HAL_GetTick(); // // 控制刷新率为 10 FPS(每 100ms 一帧) // if (now - last_draw_time < 100) // return; // last_draw_time = now; // const int y_center = 48; // const int height = 16; // const int y_top = y_center - height / 2; // y=40 // const int y_bottom = y_center + height / 2; // y=56 // float total_duration = 12.8f; // 总时间跨度 12.8 秒 // float dt_per_pixel = total_duration / 128.0f; // 每列 ≈0.1秒 // for (int x = 0; x < OLED_WIDTH; x++) // { // // 计算这一列对应的时间点(从现在往回推) // float t_back = now / 1000.0f - (OLED_WIDTH - 1 - x) * dt_per_pixel; // float phase = fmodf(t_back * current_freq, 1.0f); // uint8_t is_high = (phase < (current_duty / 100.0f)) ? 1 : 0; // // 绘制该列:高电平区域点亮 // for (int y = y_top; y <= y_bottom; y++) // { // OLED_DrawPixel(x, y, is_high); // } // } // // === 只刷新受影响的页面(page 5~7)=== // for (uint8_t page = 5; page <= 7; page++) // { // OLED_WriteCommand(0xB0 + page); // 设置页地址 // OLED_WriteCommand(0x00); // 列低4位 // OLED_WriteCommand(0x10); // 列高4位 // HAL_I2C_Mem_Write(&hi2c1, OLED_I2C_ADDR, 0x40, // I2C_MEMADD_SIZE_8BIT, // &OLED_Buffer[page * OLED_WIDTH], // OLED_WIDTH, HAL_MAX_DELAY); // } //} //void OLED_DrawStaticSquareWave(int x_start, int y_center, int height, float freq, float duty_cycle) //{ // const int y_top = y_center - height / 2; // 高电平 Y (如 40) // const int y_bottom = y_center + height / 2; // 低电平 Y (56) // const int total_width = 128 - x_start; // // // === 参数保护 === // if (freq <= 0) freq = 1.0f; // if (duty_cycle < 0) duty_cycle = 0; // if (duty_cycle > 100) duty_cycle = 100; // // === 自动缩放每个周期的像素宽度 === // int cycle_pixels; // if (freq >= 100) cycle_pixels = 5; // else if (freq >= 50) cycle_pixels = 10; // else if (freq >= 20) cycle_pixels = 15; // else if (freq >= 10) cycle_pixels = 20; // else if (freq >= 5) cycle_pixels = 30; // else if (freq >= 1) cycle_pixels = 50; // else cycle_pixels = 80; // // 限制最小宽度 // if (cycle_pixels < 10) cycle_pixels = 10; // if (cycle_pixels > 80) cycle_pixels = 80; // int high_width = (int)(cycle_pixels * (duty_cycle / 100.0f)); // if (duty_cycle > 0 && high_width == 0) high_width = 1; // if (duty_cycle >= 100) high_width = cycle_pixels; // int low_width = cycle_pixels - high_width; // if (duty_cycle < 100 && low_width == 0) low_width = 1; // int x = x_start; // // === 清除旧波形区域(防止残留)=== // for (int y = y_top; y <= y_bottom; y++) { // for (int xx = x_start; xx < 128; xx++) { // OLED_DrawPixel(xx, y, 0); // } // } // // === 绘制 2~3 个周期 === // for (int i = 0; i < 3; i++) // { // // 1. 高电平段(顶部水平线) // if (high_width > 0) { // OLED_DrawLine(x, y_top, x + high_width - 1, y_top, 1); // } // // 2. 下降沿(从高到低) // if (duty_cycle > 0 && duty_cycle < 100) { // OLED_DrawLine(x + high_width - 1, y_top, x + high_width - 1, y_bottom, 1); // } // // 3. 低电平段(底部水平线) // if (low_width > 0) { // OLED_DrawLine(x + high_width, y_bottom, x + high_width + low_width - 1, y_bottom, 1); // } // // 4. 上升沿(从低到高) // if (duty_cycle > 0 && duty_cycle < 100) { // OLED_DrawLine(x + high_width + low_width - 1, y_bottom, x + high_width + low_width - 1, y_top, 1); // } // x += cycle_pixels; // if (x >= 127) break; // 超出屏幕则停止 // } //} #include "key.h" #include "main.h" #include "tim.h" extern uint16_t current_freq; extern uint16_t current_duty; // 按键消抖函数 static uint8_t key_Debounce(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) // 假设低电平有效 { HAL_Delay(10); if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_RESET) { return 1; // 确认按下 } } return 0; } void Update_CCR(void) { uint32_t arr_val = __HAL_TIM_GET_AUTORELOAD(&htim1); // CCR = ARR * (Duty / 100.0) uint32_t CCR_new = (uint32_t)((float)(arr_val + 1) * (current_duty / 100.0f)) - 1; // 确保 CCR 不会超出 ARR if (CCR_new >= arr_val) CCR_new = arr_val - 1; if (CCR_new < 1) CCR_new = 1; // 使用 TIM1 Channel 2 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, CCR_new); } void Update_ARR(void) { uint32_t F_clk = HAL_RCC_GetPCLK2Freq(); uint32_t psc_val = htim1.Instance->PSC; // 保护除以零 if (current_freq == 0) current_freq = FREQ_MIN; uint32_t ARR_new = (F_clk / (current_freq * (psc_val + 1))) - 1; // 确保 ARR 不会溢出或过小 if (ARR_new > 0xFFFF) ARR_new = 0xFFFF; if (ARR_new < 1) ARR_new = 1; __HAL_TIM_DISABLE(&htim1); __HAL_TIM_SET_AUTORELOAD(&htim1, ARR_new); __HAL_TIM_ENABLE(&htim1); Update_CCR(); } void Key_Scan_All(void) { // PINA2: 频率增加 (F+) if (key_Debounce(GPIOA, GPIO_PIN_2) == 1) { if (current_freq < FREQ_MAX) { current_freq += FREQ_STEP; if (current_freq > FREQ_MAX) current_freq = FREQ_MAX; Update_ARR(); // 更新频率 CCR } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET); // 等待按键释放 } // PINA3: 频率减小 (F-) if (key_Debounce(GPIOA, GPIO_PIN_3) == 1) { if (current_freq > FREQ_MIN) { current_freq -= FREQ_STEP; if (current_freq < FREQ_MIN) current_freq = FREQ_MIN; Update_ARR(); // 更新频率 CCR } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_RESET); } // PINA4: 占空比增加 (D+) if (key_Debounce(GPIOA, GPIO_PIN_4) == 1) { if (current_duty < DUTY_MAX) { current_duty += DUTY_STEP; if (current_duty > DUTY_MAX) current_duty = DUTY_MAX; Update_CCR(); // 更新占空比 } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET); } // PINA5: 占空比减小 (D-) if (key_Debounce(GPIOA, GPIO_PIN_5) == 1) { if (current_duty > DUTY_MIN) { current_duty -= DUTY_STEP; if (current_duty < DUTY_MIN) current_duty = DUTY_MIN; Update_CCR(); // 更新占空比 } while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5) == GPIO_PIN_RESET); // 等待按键释放 } } 用这些把我之前的要求实现
最新发布
11-27
### C语言 `fgetc` `fput` 函数的使用说明及区别 #### 一、函数定义与功能描述 在C语言中,`fgetc()` `fputc()` 是用于处理文件输入/输出的核心函数之一。它们主要用于以字符形式读写文件。 - **`fgetc()`**: 这是一个从指定文件流中逐个读取字符的函数。当到达文件末尾时返回EOF[^1]。 - **`fputc()`**: 这是一个向指定文件流中逐个写入字符的函数。如果操作成功,则返回所写的字符;否则返回EOF[^2]。 #### 二、函数原型 以下是这两个函数的标准声明: ```c int fgetc(FILE *stream); int fputc(int char, FILE *stream); ``` - 对于`fgetc()`而言,参数`FILE *stream`表示要从中读取数据的文件指针。 - 而对于`fputc()`来说,第一个参数是要写入的具体字符(作为整数传递),第二个参数则是目标文件的指针[^3]。 #### 三、实际应用案例分析 下面通过具体例子来展示如何运用上述两个函数完成基本的任务——即从现有文本文件里提取内容并打印到屏幕上以及创建新文件并将特定消息存储进去的过程。 ##### 示例程序:利用`fgetc()`读取整个文件的内容至控制台显示出来 ```c #include <stdio.h> int main(){ //尝试打开名为"example.txt"只读模式下的文件 FILE* filePointer = fopen("example.txt","r"); if(filePointer==NULL){ printf("Error! Could not open the specified file.\n"); return -1; } int character=0; while((character=fgetc(filePointer)) != EOF ){ putchar(character); } fclose(filePointer); return 0; } ``` 此段代码展示了怎样借助循环结构配合调用`fgetc()`方法逐一获取直至遇到结束标志位为止的所有单个字母,并即时呈现在标准输出设备上。 ##### 另外一个实例:采用`fputc()`把字符串保存成新的文档 ```c #include<stdio.h> #define MAX_LEN 50 void saveStringToFile(char str[]){ FILE *fp=fopen("outputFile.txt","w+"); if(fp!=NULL){ for(int i=0;i<strlen(str);i++) { fputc(str[i], fp); } puts("\nThe string has been successfully written into outputFile.txt."); fclose(fp); }else{ perror("Failed to create/open "); } } int main(){ char inputStr[MAX_LEN]; printf("Enter any sentence:"); gets(inputStr); saveStringToFile(inputStr); return 0; } ``` 这里我们编写了一个简单的辅助函数saveStringToFile(), 它接受用户键入的一句话作为参数并通过for迭代器联合起来多次执行单一字符级别的记录动作最终形成完整的句子存档. #### 四、主要差异对比表 | 特性 | `fgetc()` | `fputc()` | |-----------------|------------------------------------|-----------------------------------| | 功能 | 从给定文件中读取下一个可用字符 | 向指定位置追加单独的一个ASCII码对应的图形表现形式 | | 返回值类型 | 整型数值 | 成功则反馈该被放置元素本身;失败给出错误指示信号(-1) | | 参数数量&含义 | 单参:指向源资料集合的对象实体地址 | 双参:前者为目标符号编码值后者关联目的容器链接标识符 | 综上所述,虽然两者都围绕着基于单位级别上的信息交换展开讨论,但是方向完全相反,一个是抽取外部资源内部化呈现过程中的桥梁纽带角色扮演者身份体现得淋漓尽致;另一个更多时候承担起构建持久化储存解决方案过程中不可或缺的重要组成部分使命担当形象塑造得栩栩如生[^2].
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值