基于STM32的风量控制器的Proteus仿真


一、风量控制器

1.题目要求

设计一个可以风量控制器进行通信的控制板,该控制板由1块OLED显示屏和8个物理按键组成,其中显示屏用来显示当前变风量控制器运行状况及风速。

其中物理按键分别的功能是灯照明开关,风量增大设置,风量减少设置,风机开关,报警开关,节能模块开关,电源开关,静音开关。

2.思路

首先,要有一个OLED显示屏,刚好Proteus里面有IIC的OLED显示屏。显示屏主要用来显示风量的大小,这里我暂定等级大小为0-15级。

然后接8个按键上去进去控制,按键识别为按下引脚识别为低电平,这里注意按键和按键间有一定的逻辑。只有当电源开关开启的时候,其他按键按下才能执行任务;只有当风机开关开启的时候,风量才能增大或者减小。静音模式和报警是相对的,只有当静音模式的时候才能按下报警按键,也只有当报警模式的时候才能按下静音模式按键。

3.仿真图

引脚说明:
主控芯片:STM32F103C8
测试Led:PC13
Uart1:9600(PA9:tx1,PA10:rx1)

OLED显示屏:SCL(PB8),SDA(PB9)

KEY按键:
KEY1(PA0)
KEY2(PA1)
KEY3(PA2)
KEY4(PA3)
KEY5(PA4)
KEY6(PA5)
KEY7(PA6)
KEY8(PA7)

在这里插入图片描述

下面是仿真视频演示:

Air volume controller

4.仿真程序

4.1 程序说明

主控芯片:STM32F103C8
HICK:64MHZ
Systick: 1ms
测试Led:PC13
Uart1:9600(PA9:tx1,PA10:rx1)

OLED显示屏:SCL(PB8),SDA(PB9)

KEY按键:
KEY1(PA0)
KEY2(PA1)
KEY3(PA2)
KEY4(PA3)
KEY5(PA4)
KEY6(PA5)
KEY7(PA6)
KEY8(PA7)

模拟RS485的串口协议如下:

灯照明开关 *KEY1H,H为0:关闭灯照明,H为1:打开灯照明
风量增大设置 *KEY21,风量增大一级,最大15级
风量减少设置 *KEY31,风量减少一级,最小0级
风机开关 *KEY4H,H为0:关闭风机,H为1:打开风机
报警开关 *KEY51,开启报警
节能模式开关 *KEY6H,H为0:关闭节能模式,H为1:开启节能模式
电源开关 *KEY7H,H为0:关闭电源,H为1:开启电源
静音开关 *KEY81,关闭报警,开启静音

注意事项:
默认电源开关和风机开关打开,只有当电源开关开启的时候,其他按键按下才能执行任务;只有当风机开关开启的时候,风量才能增大或者减小。
默认静音模式,非报警,只有当静音模式的时候才能按下报警按键,也只有当报警模式的时候才能按下静音模式按键

4.2 主函数

/* Includes ------------------------------------------------------------------*/
#include "Drv_UserSystem.h"

/**
  * @brief  main function.
  * @param  none
  * @retval none
  */
int main(void)
{
	UserSystemInit();//用户配置初始化		
	while (1)
	{				
		if (stSysTime.flg._10ms + TEN_MILLISECOND < Time_millis()) //10ms
		{
			stSysTime.flg._10ms = Time_millis();	
      Key_Scan();//按键扫描	
			OLED_Handel();//OLED显示			
		}
		if (stSysTime.flg._50ms + FIFTY_MILLISECOND < Time_millis()) //50ms
		{
			stSysTime.flg._50ms = Time_millis();				
		}
		if (stSysTime.flg._100ms + BEST_MILLISECOND < Time_millis()) //100ms
		{
			stSysTime.flg._100ms = Time_millis();	
			Receive_data_Handel();//数据接收判断
			IWDG_ReloadCounter();//清开门狗 
		}
		if (stSysTime.flg._1s + THOUSAND_MILLISECOND < Time_millis()) //1s
		{
			stSysTime.flg._1s = Time_millis();	 
			Led_Flicker();//灯光闪烁 				
	  }
  }
}

4.2 OLED显示函数

/*******************************************************************************
 * 函数名:OLED_Handel
 * 描述  :OLED显示
 * 输入  :void
 * 输出  :void
 * 调用  :初始化
 * 备注  :10ms
*******************************************************************************/
void OLED_Handel(void)
{   
	OLED_ShowString(1, 1, "Air_volume");
    OLED_ShowNum(2,12,Air_volume,2);		
}

/**
  * @brief  OLED显示字符串
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串,范围:ASCII可见字符
  * @retval 无
  */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i++)
	{
		OLED_ShowChar(Line, Column + i, String[i]);
	}
}

/**
  * @brief  OLED显示数字(十进制,正数)
  * @param  Line 起始行位置,范围:1~4
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~4294967295
  * @param  Length 要显示数字的长度,范围:1~10
  * @retval 无
  */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i++)							
	{
		OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
	}
}

4.3 按键函数

/*******************************************************************************
 * 函数名:Key_Scan
 * 描述  :按键扫描
 * 输入  :void
 * 输出  :void
 * 调用  :10ms
 * 备注  :
 *******************************************************************************/
void Key_Scan(void)
{
    if(Key7_IN_Read() == 0)//电源开关	
		{
			if(!KeyState.Press7)
			{
				if(KeyState.Key7flag)
				{
					KeyState.Key7flag = 0;
					power_en = 0; 
                    printf("\r\n关闭电源");							
				}
				else
				{
					KeyState.Key7flag = 1;
					power_en = 1;
                    printf("\r\n开启电源");				
				}				
			}
			KeyState.Press7 = 1;
		}		
    else
		{		
			KeyState.Press7 = 0;
		}		
	
		if(power_en)
		{
				if(Key1_IN_Read() == 0)//灯照明开关
				{
					if(!KeyState.Press1)
					{
						if(KeyState.Key1flag)
						{
							KeyState.Key1flag = 0;
							printf("\r\n关闭灯照明");							
						}
						else
						{
							KeyState.Key1flag = 1;
							printf("\r\n打开灯照明");					
						}				
					}
					KeyState.Press1 = 1;
				}		
				else
				{		
					KeyState.Press1 = 0;
				}	
				
				if(Air_blower_en)
				{
						if(Key2_IN_Read() == 0)//风量增大设置
						{
							if(!KeyState.Press2)
							{
								Air_volume++;
								if(Air_volume>15)
								{
									Air_volume = 15;
								}
								printf("Air_volume = %d\r\n",Air_volume);
								printf("\r\n风量增大一级");					
							}
							KeyState.Press2 = 1;
						}		
						else
						{		
							KeyState.Press2 = 0;
						}		
						
						if(Key3_IN_Read() == 0)//风量减少设置
						{
							if(!KeyState.Press3)
							{
								if(Air_volume == 0)
								{
								}
								else
								{
								    Air_volume--;
								}
								printf("Air_volume = %d\r\n",Air_volume);				
								printf("\r\n风量减小一级");			
							}
							KeyState.Press3 = 1;
						}		
						else
						{		
							KeyState.Press3 = 0;
						}
				}		
				
				if(Key4_IN_Read() == 0)//风机开关
				{
					if(!KeyState.Press4)
					{
						if(KeyState.Key4flag)
						{
							KeyState.Key4flag = 0;
							Air_blower_en = 0;
							printf("\r\n关闭风机");							
						}
						else
						{
							KeyState.Key4flag = 1;
							Air_blower_en = 1;
							printf("\r\n打开风机");				
						}				
					}		
					KeyState.Press4 = 1;
				}		
				else
				{		
					KeyState.Press4 = 0;
				}	

				if(Give_an_alarm == 0)
				{					
						if(Key5_IN_Read() == 0)//报警开关	
						{
							if(!KeyState.Press5)
							{
								Give_an_alarm = 1;
								printf("\r\n开启报警");			
							}
							KeyState.Press5 = 1;
						}		
						else
						{		
							KeyState.Press5 = 0;
						}	
			  }
				
				if(Key6_IN_Read() == 0)//节能模式开关
				{
					if(!KeyState.Press6)
					{
						if(KeyState.Key6flag)
						{
							KeyState.Key6flag = 0;
							printf("\r\n关闭节能模式");							
						}
						else
						{
							KeyState.Key6flag = 1;
							printf("\r\n开启节能模式");				
						}				
					}		
					KeyState.Press6 = 1;
				}		
				else
				{		
					KeyState.Press6 = 0;
				}		

				if(Give_an_alarm == 1)
				{					
						if(Key8_IN_Read() == 0)//静音开关	
						{
							if(!KeyState.Press8)
							{
								Give_an_alarm = 0;
								printf("\r\n关闭报警,开启静音");					
							}
							KeyState.Press8 = 1;
						}		
						else
						{		
							KeyState.Press8 = 0;
						}
			  }				
			}				
}

三、总结

今天主要讲了基于STM32的风量控制器的Proteus仿真。

感谢你的观看!

在这里插入图片描述

#include <stdio.h> #include "ry72xx_system.h" #include "ry_gpio_drv.h" #include "uart0_hal_drv.h" #include "ry_rtc_drv.h" #include "ry_delay_drv.h" #include "ry_systick_drv.h" #include "systick_config.h" /** **************************************************************************************** * @addtogroup demo_if_systick * @copydoc systick_main.c * * @brief 内核模块系统节拍定时器测试例程主程序 * @details 主要包含内核模块系统节拍定时器功能测试,main函数入口 * * @section 操作步骤: * -# 选择系统时钟并初始化。 * -# 初始化串口和GPIO口并所有LED闪烁输出,配置系统节拍定时器的节拍时长和系统节拍定时器中断回调函数,在回调函数中翻转LED1灯,同时全局变量节拍个数增加1。 * -# 然后等待LB按键按下,第一次LB被按下后开启系统节拍定时并打印输出状态信息。 * -# 第二次LB被按下后关闭系统节拍定时并打印输出第一次与第二次之间的时长信息。 * -# 第三次LB被按下则重新开启系统节拍定时,第四次按下则关闭系统节拍定时,以此类推。 * * @section 实验现象: * -# 打印测试开始信息,然后所有LED闪烁。 * -# 奇数次按下LB按键(LED6翻转),串口打印输出系统节拍定时开启的状态信息。 * -# 偶数次按下LB按键(LED6翻转),串口打印输出系统节拍定时关闭的状态信息。 * * @{ * **************************************************************************************** */ #define SYSTICK_TRIG_TIME 100000 /** 系统节拍时长,单位:微秒(Us) */ uint32 sys_load_val = 0x0; /* 系统节拍定时器加载值 */ uint32 sys_run_time = 0x0; /* 系统运行时长————运行期间的节拍个数 */ void systick_int_callback(void) { sys_run_time++; gpio_out_toggle(LED1_PA20); } /** * \brief 主程序 */ int main(void) { uint8 i; /* 模块电源配置 */ pmu_peri_clear_all(); pmu_power_adc_config(POWER_ADC_INTERNAL_VDD); pmu_power_usb_config(POWER_USB_INTERNAL_OFF); pmu_power_gpioc_config(POWER_GPIOC_INTERNAL_VDD); pmu_power_gpiod_config(POWER_GPIOD_INTERNAL_VDD); system_clk_init(IMO_CLK_48MHz, HFCLK_DIV1); /* 初始化时钟 */ ry_delay_ms(3000); /* 串口初始化的宏定义在编译器Options的Preprocessor Symbols中 */ #ifdef PRINT_UART0 uart0_hal_init(); #elif PRINT_UART1 uart1_hal_init(); #endif PRINT("systick test is start!!!\r\n"); /* LED灯初始化配置 */ gpio_cfg_output(LED1_PA20, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(LED1_PA20); gpio_cfg_output(LED2_PA21, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(LED2_PA21); gpio_cfg_output(LED3_PA22, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(LED3_PA22); gpio_cfg_output(LED4_PA23, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(LED4_PA23); gpio_cfg_output(LED5_PA24, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(LED5_PA24); gpio_cfg_output(LED6_PA25, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(LED6_PA25); /* 三色LED灯的颜色初始化配置 */ gpio_out_set(B_C0_PA6); gpio_cfg_output(B_C0_PA6, RY_GPIO_DRIVE_S0_S1); gpio_out_set(G_C0_PA7); gpio_cfg_output(G_C0_PA7, RY_GPIO_DRIVE_S0_S1); gpio_out_set(R_C0_PA8); gpio_cfg_output(R_C0_PA8, RY_GPIO_DRIVE_S0_S1); gpio_out_clear(R_C0_PA8); /* 使能三色LED灯的红色灯 */ for(i = 0; i < 30; i++) /* 所有LED闪烁 */ { gpio_out_multi_toggle(PA_GROUP, LED1_PA20_MASK|LED2_PA21_MASK|LED3_PA22_MASK|LED4_PA23_MASK|LED5_PA24_MASK|LED6_PA25_MASK); ry_delay_ms(100); } gpio_cfg_input(LB_BTN1_PA13, RY_GPIO_PULLUP_5K); if(systick_set_load(SYSTICK_TRIG_TIME)) /* 设置系统节拍 */ { PRINT("systick load val error, please comfirm it!\r\n"); } systick_int_init(systick_int_callback); /* 配置中断回调函数 */ while(1) { ry_delay_ms(1); if(gpio_in_read(LB_BTN1_PA13) == 0x0) { while(gpio_in_read(LB_BTN1_PA13) == 0x0); gpio_out_toggle(LED6_PA25); ry_delay_ms(1); if(gpio_in_read(LED6_PA25)) { PRINT("systick start times:%dUs\r\n", sys_run_time*SYSTICK_TRIG_TIME); systick_enable(); /* 开启系统节拍定时器 */ } else { systick_disable(); /* 关闭系统节拍定时器 */ PRINT("systick stop times:%dUs\r\n", sys_run_time*SYSTICK_TRIG_TIME); systick_set_count(0); /* 重置定时器计数值 */ sys_run_time = 0; } } } }这个芯片的systick配置与stm32的systick配置有什么差别?
03-21
<think>嗯,用户问的是瑞萨芯片的SysTick配置和STM32的有什么差别。我需要先回忆一下这两个芯片的SysTick模块。首先,SysTick是ARM Cortex-M系列的内核定时器,所以理论上应该都是遵循ARM的设计,但不同厂商可能有不同的库函数和配置方法。 用户提供的代码里,瑞萨的例程用了systick_set_load、systick_int_init这些函数,而STM32通常用标准库或者HAL库的函数,比如SysTick_Config。那两者的配置流程可能有差异。比如,瑞萨的代码里设置加载值用的是systick_set_load,而STM32可能直接操作LOAD寄存器或者用库函数。 中断回调方面,瑞萨的例程里显式地注册了一个回调函数systick_int_callback,而STM32通常需要用户在中断服务例程中自己处理,比如在SysTick_Handler里写代码。不过,HAL库可能有回调机制,但通常需要用户自己实现。 时钟源方面,SysTick一般可以选择内核时钟或者外部时钟,但具体实现可能不同。瑞萨的代码里初始化系统时钟为48MHz,而STM32可能需要配置不同的时钟树,比如使用HSE或HSI作为源,然后分频。 另外,用户代码里提到了SYSTICK_TRIG_TIME是100000微秒,也就是100ms。这个加载值的计算可能涉及不同的时钟频率和分频设置。在STM32中,通常SysTick的时钟频率是系统时钟频率(比如72MHz)的八分之一,或者直接系统时钟,这取决于配置。瑞萨的可能直接使用系统时钟,所以加载值的计算方式不同。 还有,瑞萨的代码里用了systick_enable和systick_disable来控制定时器的启停,而STM32可能通过控制CTRL寄存器的使能位,或者使用库函数SysTick_Enable和SysTick_Disable。 此外,中断的配置方式可能不同。瑞萨的代码里调用了systick_int_init来设置中断回调,而STM32可能需要手动启用中断,比如在NVIC中设置优先级,或者在初始化函数中设置。 最后,用户提到的应用场景是按键控制定时器的启停,并计算运行时间。这可能涉及到全局变量来计数节拍,这在两种芯片上实现方式类似,但具体的中断处理和定时器操作函数不同。 需要总结这些差异点,分点说明,确保结构清晰,用户能理解两者的配置流程、中断处理、时钟源选择、库函数差异等方面。</think>以下是瑞萨芯片(示例代码)与STM32的SysTick配置的主要差异分析,分步骤说明: --- ### **一、硬件基础差异** 1. **内核架构** - 两者均为ARM Cortex-M系列,SysTick模块属于ARM内核标准组件,基础功能一致(24位递减计数器、可编程重装载值)。 - **差异点**:瑞萨芯片可能通过厂商自定义的驱动库(如`ry_systick_drv.h`)封装操作,而STM32更多依赖标准CMSIS接口或HAL库。 --- ### **二、配置流程差异** #### **瑞萨芯片配置流程**(示例代码): 1. **设置节拍时长** ```c systick_set_load(SYSTICK_TRIG_TIME); // 直接传入微秒级时间 ``` - 通过`SYSTICK_TRIG_TIME`(单位:微秒)自动计算重装载值,隐含依赖系统时钟频率(如48MHz)。 2. **中断回调注册** ```c systick_int_init(systick_int_callback); // 显式绑定回调函数 ``` - 需手动注册中断回调函数,中断触发时自动调用`systick_int_callback`。 3. **启停控制** ```c systick_enable(); // 开启定时器 systick_disable(); // 关闭定时器 ``` #### **STM32配置流程**(典型HAL库): 1. **直接操作寄存器或CMSIS函数** ```c SysTick_Config(SystemCoreClock / 1000); // 配置1ms节拍 ``` - 需手动计算重装载值(基于`SystemCoreClock`,单位:Hz)。 2. **中断处理** - 需在`SysTick_Handler()`中实现中断逻辑: ```c void SysTick_Handler(void) { HAL_IncTick(); // HAL库自动更新时间基准 user_callback(); // 用户自定义逻辑 } ``` 3. **启停控制** ```c __disable_irq(); // 关闭中断 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 停止计数器 ``` --- ### **三、关键差异总结** | **特性** | **瑞萨芯片(示例)** | **STM32** | |------------------|---------------------------------------------|------------------------------------| | **时钟源** | 依赖`system_clk_init()`设置的系统时钟(如48MHz) | 默认使用内核时钟(`HCLK`),可配置分频 | | **时间单位** | 直接使用微秒(`SYSTICK_TRIG_TIME`) | 需手动计算节拍数(基于`SystemCoreClock`) | | **中断绑定** | 通过`systick_int_init()`注册回调 | 需在`SysTick_Handler()`中实现逻辑 | | **库封装层级** | 高度封装的厂商专用API(如`ry_systick_drv.h`) | CMSIS标准接口或HAL库函数 | --- ### **四、附加差异说明** 1. **中断优先级** - 瑞萨代码未显式设置优先级(可能默认配置),而STM32通常需通过`NVIC_SetPriority()`配置。 2. **时间计算方式** - 瑞萨示例通过`sys_run_time*SYSTICK_TRIG_TIME`直接累加微秒时间,STM32需依赖`HAL_GetTick()`等函数获取毫秒级时间。 3. **硬件依赖** - 瑞萨的`systick_set_load()`可能内部处理了时钟分频,而STM32需开发者明确分频系数(如`SysTick_Config(SystemCoreClock / 1000)`)。 --- ### **五、实验现象对比** - **瑞萨**:通过按键控制SysTick启停,LED1周期性翻转,串口打印累计时间(单位:微秒)。 - **STM32**:类似功能需自行实现时间累计逻辑,通常以毫秒为单位(通过`HAL_Delay()`或自定义计数器)。 --- **总结**:瑞萨的SysTick配置更依赖厂商封装的API,简化了时间单位转换和中断绑定;STM32则更接近底层寄存器或标准库,需开发者处理更多细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaobuding_QAQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值