项目介绍
最近接到一个项目,需要制作一台咖啡机,家庭使用需求包括触摸屏操作,功能要好,但目前市面上可选的串口屏可能不太理想,幸运的是, 这个项目我可以自行决定使用哪种MCU,同时也可以决定使用哪种屏幕,因此我选择了这种简单易用的STM32 MCU,串口屏则选择了STONE的触摸屏串口屏,屏幕设计简洁易用,我的STM32 MCU只需通过UART通信即可与之配合使用。
STONE串口屏可通过MCU的串口进行通信。同时,该串口屏的UI界面逻辑设计可直接使用STONE官网提供的STONE TOOL Box进行设计,非常方便。因此我将在此咖啡机项目中采用该方案。
同时,我将简要记录基本开发流程。由于这是公司项目,仅记录简单演示,不提供完整代码。
该网站提供了关于型号、使用方法及设计文档的丰富信息,以及视频教程。此处不再赘述。
咖啡机串口屏功能介绍
本项目具备以下功能:
- 显示当前时间和日期
- 串口屏上设有四个按钮,分别对应美式咖啡、拿铁、卡布奇诺和意式浓缩咖啡。
- 显示当前剩余咖啡豆、牛奶和咖啡糖的数量
- 文本显示框显示当前状态
基于这些概念,您可以设计用户界面。STONE的触摸屏在UI设计上相对简单,用户可通过Photoshop软件设计出良好的UI界面和按钮效果,再通过STONE TOOL Box将设计好的图片导入屏幕,并使用STONE TOOL Box的逻辑和串行数据添加按钮,返回值正常,开发非常简单。
为STONE制作UI图片
根据功能需求,我制作了以下两个UI显示界面,一个是主界面,另一个是按钮效果。
STONE TOOL Box的使用
目前,STONE提供了TOOL Box。打开此TOOL创建新项目,然后导入设计的UI显示图片,并添加自己的按钮、文本显示框等。
在 STONE TOOL Box 中添加按钮和显示组件的效果如下:
STONE TOOL Box 具有模拟显示功能,通过该功能可查看 UI 界面的操作效果:
至此,我的 UI 显示已完成,接下来只需编写 MCU 代码。
将 STONE TOOL Box 生成的文件下载到串口屏上即可查看实际效果。
STM32F103RCT6
STM32F103RCT6 MCU 具有强大的功能。以下是 MCU 的基本参数:
- 系列:STM32F10X
- 内核:ARM – CORTEX32
- 速度:72 MHz
- 通信接口:CAN、I2C、IrDA、LIN、SPI、UART/USART、USB
- 外设:DMA、电机控制PWM、PDR、POR、PVD、PWM、温度传感器、WDT
- 程序存储容量:256KB
- 程序存储类型:FLASH
- RAM容量:48K
- 电压 – 电源(Vcc/Vdd):2 V ~ 3.6 V
- 振荡器:内部
- 工作温度:-40°C ~ 85°C
- 封装/外壳:64-lqfp
在本项目中,我将使用STM32F103RCT6的UART、GPIO、看门狗和定时器。
这些外设的开发过程如下所示。
STM32使用Keil MDK软件进行开发,相信大家对该软件已较为熟悉,因此不再赘述其安装方法。
STM32可通过j-link或st-link等仿真工具进行在线仿真。下图为本项目所使用的STM32电路板:
UART 串口
STM32F103RCT6 拥有多个串口。本项目中使用了串口通道 PA9/PA10,串口波特率设置为 115200。
oid uart_init(u32 bound){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //Clock
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);//GPIOA.9
//USART1_RX GPIOA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);//GPIOA.10
//Usart1 NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//USART
USART_InitStructure.USART_BaudRate
u8 USART_RX_END=0;
void USART1_IRQHandler(void) //Uart1 handler
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
if(USART_RX_END==0)
{
Res =USART_ReceiveData(USART1);
USART_RX_BUF[USART_RX_STA]=Res ;
USART_RX_STA++;
if(USART_RX_STA>8)
{
USART_RX_END=1;
}
}
}
}
GPIO
在本项目用户界面中,共有四个按钮,实际上对应制作四种咖啡。在咖啡机中,通过控制传感器和继电器实现不同咖啡的咖啡豆数量、牛奶消耗量及水流量的控制,而我仅先控制GPIO引脚。
void PAD_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE); //ENABLE CLOCK
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //–>PB config
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // PP OUT
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO SPEED 50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3); //config out hight
定时器
在初始化定时器时,指定频率分频系数PSC,这里是我们的系统时钟(72MHz)用于频率分频
然后指定重新加载值arr,这意味着当定时器达到此arr时,定时器将重新加载其他值。
例如,当我们设置定时器为计数模式时,定时器计数值等于 arr,并将被清零并重新计算
定时器计数值被重新加载,这被称为一次更新
计算更新时间公式 Tout = ((arr+1)*(PSC +1))/Tclk
公式推导:Tclk 是定时器的时钟源,这里为 72MHz
我们将分配的时钟频率除以频率分频值PSC,然后将Tclk除以PSC+1,最终定时器的频率为Tclk/(PSC+1) MHz
这里所说的频率是指Tclk在PSC+1 M个数字(1M=10^6)上的周期, 每个数字对应的时间为 PSC +1 /Tclk,不难理解频率的倒数即为周期,这里每个数字的周期为 PSC +1 /Tclk 秒然后我们从 0 到 arr 的循环为 (arr+1)*(PSC +1)/Tclk
例如,设 arr=7199 和 PSC =9999
我们将72MHz除以9999+1,等于7200Hz
这相当于每秒9000个计数,每个计数为1/7200秒
因此,我们记录9000个数值以触发定时器更新,(7199+1) * (1/7200) = 1秒,即1秒后触发一次更新。
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //clock
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE );
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
I needed a timer interrupt to time how much coffee and milk I had left, so I wrote the code for the timer driver and wrote a timer interrupt function:
//handler
void TIM3_IRQHandler(void) //TIM3
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
看门狗
为了防止程序运行时系统崩溃,我添加了看门狗。实际上,所有使用MCU的项目通常都会使用看门狗。
STM32内置了两个看门狗,提供了更高的安全性、时间精度和灵活性。两个看门狗设备(独立看门狗和窗口看门狗)可用于检测并解决由软件错误引起的故障。当计数器达到预设超时值时,将触发中断(仅窗口看门狗)或系统复位。
独立看门狗(IWDG):
由专用低速时钟(LSI)驱动,即使主时钟故障仍能正常工作。
适用于需要看门狗在主程序之外完全独立工作且对时间精度要求较低的场景。窗口看门狗(WWDG):
由APB1时钟经过频率分频后供电。通过可配置的时间窗口检测应用程序操作的异常延迟或过早执行。适用于需要看门狗在精确时间窗口内工作的程序。
int main(void)
{
delay_init(); //delay init
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC INIT
uart_init(115200); //UART INIT
PAD_INIT(); //Light Init
IWDG_Init(4,625);
while(1)
{
if(USART_RX_END)
{
switch (USART_RX_BUF[5])
{
case Espresso:
CoffeeSelect(Espresso,USART_RX_BUF[8]);
break;
case Americano:
CoffeeSelect(Americano,USART_RX_BUF[8]);
The Main logic in the Main function is as follows:
u8 timer_cnt=0;
void TIM3_IRQHandler(void) //TIM3
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update );
timer_cnt++;
if(timer_cnt>=200)
{
milk_send[6]=milk();
最后,在定时器中断中添加以下代码:
在定时器中断中,我的目标是检查剩余的咖啡和牛奶量,然后通过串口将检测到的值发送至串口屏。
测量剩余牛奶和咖啡豆的量通常通过传感器实现。简单方法包括压力传感器,通过测量牛奶和咖啡豆的当前重量来确定剩余量。
写在最后
本文仅记录了项目开发的简单过程。考虑到公司项目的保密性,文中使用的UI显示界面均为本人自行设计,并非该项目实际使用的UI显示界面。
STM32的代码部分仅添加了MCU的外设驱动程序和相关逻辑代码。同样出于公司项目保密考虑,具体核心技术部分不予披露,请谅解。然而,根据我提供的代码,与STONE串口屏配合使用,我的软件工程师朋友只需在我的代码框架基础上添加关键技术部分,即可在数天内完成项目。