tag 标签: stm32f103

相关帖子
相关博文
  • 热度 15
    2022-5-17 08:53
    2395 次阅读|
    0 个评论
    如何触发STM32F103C8T6的外部中断?
    中断是MCU的特征之一,STM32F103C8T6也不例外。以下通过STM32F103C8T6 Blue Pill外接一个按钮,向MCU触发外部中断。 STM32F103C8T6中断特征 中断有硬件中断和软件中断两种,当一个中断发生时,相应的中断服务程序(ISR)和中断处理程序就开始运行了。其中,ISR的指令必须尽可能小,ISR中也不能出现delay ()之类的阻塞函数。 STM32 Blue Pill或者STM32F103C8T6 MCU指定NVIC来管理所有外部中断和外设中断,其应用采用了Arduino IDE。因此,我们可使用相同的句法来配置外部中断,这可通过以下称作 “attachInterrupt ()” 的句法给出: attachInterrupt(digitalPinToInterrupt(pin), ISR, mode); 该句法的第一个参数 digitalPinToInterrupt(pin) 代表预期外部中断的MCU引脚,例如,如果按钮连接于PA0,该参数就变成“digitalPinToInterrupt(PA0)”。 第二个参数是ISR函数,该函数必须具有 void 返回类型,不能代任何参数。 第三个函数是是触发中断的模式,例如,信号突变到哪个点位时,必须触发一个中断。Arduino环境下,该参数通常为5个用户预定义的常数,但STM32须从如下3个选项中选取: CHANGE: 在引脚数值改变时触发中断。 RISING: 当引脚数值从LOW 上升到 HIGH 时触发中断。 FALLING: 当引脚数值从 HIGH 回落到 LOW 时触发中断。 用按钮为STM32 MCU触发一个中断 本项目所需元件包括:STM32F103C8T6蓝丸版、按钮、USB-UART转换器、USB电缆,杜邦线。 首先,将按钮连接到 PA0 引脚,并通过一个4.7KΩ电阻器上拉到HIGH ,另一引脚接 GND。 以下代码用以激励 PA0 引脚的中断,button_ISR用来切换LED。 int ledPin = PC13; int buttonPin = PA0; int ledToggle; int previousState = HIGH; unsigned int previousPress = 0; volatile int buttonFlag; int buttonDebounce = 20; void setup() { pinMode(ledPin, OUTPUT); pinMode(buttonPin, INPUT); attachInterrupt(digitalPinToInterrupt(buttonPin), button_ISR, CHANGE); } void loop() { buttonDebounce && buttonFlag) { previousPress = millis(); if(digitalRead(buttonPin) == LOW && previousState == HIGH) { ledToggle =! ledToggle; digitalWrite(ledPin, ledToggle); previousState = LOW; } else if(digitalRead(buttonPin) == HIGH && previousState == LOW) { previousState = HIGH; } buttonFlag = 0; } } void button_ISR() { buttonFlag = 1; } 本项目演示了STM32F103C8T6 Blue Pill外部中断的操作,每按一次按钮就触发一次中断,STM32 MCU就执行一次简单ISR,返回来切换LED状态。
  • 热度 3
    2021-4-17 18:09
    2947 次阅读|
    1 个评论
    用两张图,详解FreeModbus在单片机串口上的数据收发过程-FreeModbus从站设计(9)
    FreeModbus从站设计(9)-详解FreeModbus在单片机串口上的数据收发过程 关键词:FreeModbus STM32F103 CubeMX HAL库 串口 1.引言 在上一篇文章中,主要阐述了vMBPortSerialEnable()这个函数如何基于HAL库调度单片机串口的收发,感觉还是不是很清晰,因此,孔丙火(微信公众号:孔丙火)在这一篇文章中,重点捋一下串口的收发函数调用关系,以求有有一个清晰的脉络。 2.函数调用的基本框架 直接上图,更清晰,接收过程如图1所示,发送过程如图2所示。 图1 接收过程起源于vMBPortSerialEnable()函数的调用,此时,该函数将串口设置位接收状态,即使能接收中断,禁止发送中断。从图中可以清晰的看出,需要修改的地方就是接收中断的回调函数和portserial.c和porttimer.c中的几个函数。至于何时调用vMBPortSerialEnable()函数,孔丙火(微信公众号:孔丙火)认为,我们是不需要关心的,只要按照之前的文章,把FreeModbus的代码添加到keil工程中,FreeModbus协议栈会进行调度。接收过程是一个字节一个字节进行接收的,当协议栈检测到定时器超时,则认为一个完整的数据帧接收完毕,开始进入数据处理的阶段,数据处理完成后,则进行回复数据的发送。 图2 发送过程同样起源于vMBPortSerialEnable()函数的调用,此时,该函数将串口设置位发送状态,即使能发送中断,禁止接收中断。从图中可以清晰的看出,需要修改的地方就是发送中断的回调函数和portserial.c中的几个函数。至于何时调用vMBPortSerialEnable()函数,孔丙火(微信公众号:孔丙火)认为,我们是不需要关心的,只要按照之前的文章,把FreeModbus的代码添加到keil工程中,FreeModbus协议栈会进行调度。发送过程同样是一个字节一个字节进行的,在xMBRTUTransmitFSM()函数中,会检测是否还有需要发送的数据,若没有数据需要发送了,则会调用vMBPortSerialEnable()函数,再次将串口设置为接收状态。作为Modbus从站,串口大部分时间是处于接收状态的。 3.总结 在这篇文章中,孔丙火(微信公众号:孔丙火)接着上一篇文章的思路,用两张图把FreeModbus在单片机串口上数据收发流程进行了梳理,脉络更加清晰。有了这样一个思路,可以更好地理解,移植FreeModbus的时候,为什么需要修改portserial.c和porttimer.c中的函数,和为什么需要修改串口中断的回调函数。从这篇文章中,也可以看出,采用HAL库是比较简单的,像是中断处理这些内容库函数都已经处理好了,很方便,可以提高开发效率。 文章在公众号( 孔丙火 )同步推出,欢迎查看更多系列文章。 单片机、ARM、现场总线、PLC、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
  • 热度 21
    2021-3-21 21:57
    2694 次阅读|
    0 个评论
    FreeModbus 从站设计( 8 ) - 用 HAL 库函数理清 Modbus 的数据收发流程 关键词: FreeModbus STM32F103 CubeMX HAL 库 1. 基本框图 如图 1 所示, HAL 库的函数中,与 Freemodbus 协议栈相关的,主要是定时器和串口的操作部分。孔丙火(微信公众号:孔丙火)认为,可以这样简单描述:在协议栈完成初始化后,就将串口( RS485 )设置为接收状态,等待主站的数据,当接收到主站的一个字节的数据后,开启定时器,在 3.5 个字符周期内如果接收到了第二个字节的数据,则将定时器清零重新开始计时,若果 3.5 个字符周期内没有接收到新的字节,则认为一帧数据接收完毕,开始处理数据,并相应地发送回复数据,回复数据也是逐个字节进行发送的。 图 1 2. 接口函数 2.1 vMBPortSerialEnable() 这个函数定义在 portserial.c 中,所有的调用均在 mbrtu.c 和 mbascii.c 中,由于这里我们只实现 RTU ,因此,只关注 mbrtu.c 中调用。在 mbrtu.c 中,共有 5 处调用此函数,分别在 eMBRTUStop() 、 eMBRTUStop() 、 eMBRTUSend() 、 xMBRTUTransmitFSM() 中,这个函数的作用,就是使能或禁止串口的接收或发送中断,状态的转换是根据协议栈的状态机来进行的,孔丙火(微信公众号:孔丙火)认为,看一看 Modbus 协议文本中的状态机图,有助于深入理解协议的运行。基于以上理解, vMBPortSerialEnable() 函数的代码如下: 图2 HAL_UART_Receive_IT(&huart2,&ucUsrUart2Rxbuf,1) ,这个函数是 HAL 库函数,孔丙火(微信公众号:孔丙火)认为,可以这样理解,以中断方式通过串口 2 接收一个字节的数据,存在变量 ucUsrUart2Rxbuf 中,这里取得是 ucUsrUart2Rxbuf 的地址指针。 __HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE) 这个函数是禁止接收中断。在这个函数下面,写了这样一个语句: huart2.RxState = HAL_UART_STATE_READY; 同样的,在发送部分有: huart2.gState = HAL_UART_STATE_READY; 下面简单阐述一下作用。 最初开始弄 FreeModbus 移植的时候,在网上查阅了一些资料,有的是基于 HAL 库的,有的不是,有的是交叉使用,由于学习了一段时间的 HAL 库,感觉其还是比较易用的,孔丙火(微信公众号:孔丙火)就想着能不能完全用 HAL 库的函数来实现的 FreeModbus 移植,于是就有后来的实践。 加这样一个语句,是为了解决在实际调试过程中碰到的问题。刚开始调试的时候发现,单片机作为从站,只能接收一帧数据,第 2 帧以后的数据,不再有反应。由于是调用 HAL_UART_Receive_IT(&huart2,&ucUsrUart2Rxbuf,1); 来使能接收非空中断,调用 __HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE); 来禁用接收非空中断,那就要从 HAL_UART_Receive_IT() 这个函数开始说起,在以前的文章中,孔丙火(微信公众号:孔丙火)说过,这个函数本质上是一个配置函数,配置好接收缓冲区的指针和一次接收的字节数,把这个函数贴出来看一下: 图 3 可以看出,这个函数的主要功能就是设置接收缓存的指针和字节数,然后使能接收非空中断,但在一开始有一个条件判断, RxState ,这其实是对串口接收状态的一个管理,当串口中断方式接收已经被配置过了、接收过程还没有完成的时候,是不能再次配置的,防止接收数据出错。 在这个函数中,配置完了以后,就将 RxState 的状态置为了 HAL_UART_STATE_BUSY_RX ,这个状态是在串口中断处理函数中完成数据接收后,重新设为 HAL_UART_STATE_READY 。在本文的程序中,当从站接收数据完成后(定时器动作,表示一个完整的帧接收完成),协议栈需要将接收非空中断禁掉,但这之前串口仍处于接收状态(调用过 HAL_UART_Receive_IT(&huart2,&ucUsrUart2Rxbuf,1) ),因此 RxState 的状态是 HAL_UART_STATE_BUSY_RX ,这样在下次转为接收时,无法 HAL_UART_Receive_IT ()来进行配置,就是由于上面提到的图 3 红框中的条件判断。因此,在发现此问题后,加了这个语句,确保后续能正常接收,并且这样可以不用修改 HAL 库本身的函数。发送部分是同样的道理。 下一篇精彩继续。 文章在公众号( 孔丙火 )同步推出,欢迎查看更多系列文章。 单片机、 PLC 、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
  • 热度 4
    2021-3-20 13:07
    2526 次阅读|
    0 个评论
    STM32F103、FreeModbus从站设计(7)-如何让RTU的定时器正常工作起来 关键词:Modbus FreeModbus STM32F103C8T6 CubeMX 移植 1.基本原理 在CubeMX工程配置中,已经将定时器2(TIM2)的时钟周期(可以理解为心跳一下)设为50us,Counter Period(产生中断)暂时设为了35,也就是说50us×35=1750us产生一次中断,这个时间就是判断RTU中帧间隔的标准。但在Freemodbus协议栈(遵循Modbus国标)中,这个时间不是固定的,在波特率小于19200bps时,需要具体计算这个时间,当波特率大于或等于19200bps时,这个时间固定为1750us,如图1所示。因此,孔丙火(微信公众号:孔丙火)认为,把它设为固定值是不方便的,当波特率修改的时候,还要单独修改此参数,可以用一个变量来设置此参数,变量的值随波特率而改变,这也有利于后期通信参数修改的程序。 图1 2.代码修改 在mbrtu.c中,有一个eMBRTUInit()函数,在协议栈初始化的时候被调用,在图2中可以看出,t3.5的计算方法。计算完了以后,调用xMBPortTimersInit()这个函数,这个函数在porttimer.c中,如果参数是写死的,CubeMX本身已经生成了定时器的初始化函数,这个函数是不用写的,直接返回True就行了。孔丙火(微信公众号:孔丙火)这里需要根据这个计算结果修改通信参数,因此要用一下这个函数。 图2 在modbus_app.c中定义一个全局变量uint16_t usUsrTimeOutCount;//modbus定时器计数周期(50us的个数),在xMBPortTimersInit()中,将usTimerT35_50us的值赋给usUsrTimeOutCount,然后在函数MX_TIM2_Init()中用这个值对TIM2进行初始化。xMBPortTimersInit()的代码如图3所示。MX_TIM2_Init()修改后的代码如图4所示。孔丙火(微信公众号:孔丙火)提醒,这样修改之后,在main.c中MX_TIM2_Init()这个函数的调用必须在eMBInit( MB_RTU, ucUsrSlaveAddress, 1, ulUsrBaudRate, eUsrParity );之后。在main.c中的初始化阶段的调用关系如图5所示。 图3 图4 图5 文章在公众号( 孔丙火 )同步推出,欢迎查看更多系列文章。 单片机、ARM、现场总线、PLC、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
  • 热度 4
    2021-3-18 21:34
    2551 次阅读|
    1 个评论
    STM32F103、FreeModbus从站设计(6)-让串口和Modbus初始化的参数同步起来
    FreeModbus从站设计(6)-让串口和Modbus初始化的参数同步起来 关键词:Modbus FreeModbus STM32F103C8T6 CubeMX 移植 1.基本原理 在这一篇文章中,孔丙火(微信公众号:孔丙火)主要介绍协议栈初始化,以及与串口相关的代码修改。串口的初始化函数是CubeMx自动生成的,其波特率等参数最好与freemodbus协议栈初始化参数保持一致,这样方便程序的维护和功能扩展,例如,当需要修改通信的波特率的时候,只需要修改一处就可以了,不用先修改串口的初始化参数,再修改协议栈的参数,这是一个实战例程与纯教程的区别。 本文的总体思路:创建一些全局变量,用于存储通信参数,串口、定时器、Freemodbus协议栈的初始化,都采用这些变量,需要更改的时候,只需改变这些变量的值即可。另外,这些通信参数后续计划存储在flash里面,定时器的参数会随波特率的不同而改变,这些在后续的文章中会阐述。 2.代码修改 2.1关于assert的说明 在Freemodbus的协议栈中,很多地方使用了assert,因此,在Cube生成工程的时候,孔丙火(微信公众号:孔丙火)认为,最好使能enable assert选项,如果没有使能,编译的时候可能出错,这个时候可以勾掉MicroLIB,因为MicroLIB不支持assert。 2.2宏的修改 (1)MB_RTU_ENABLED、MB_ASCII_ENABLED、MB_TCP_ENABLED 这里我们实现的是RTU,只需将MB_RTU_ENABLED定义为1即可。孔丙火(微信公众号:孔丙火)顺便说一句,在基于串口的Modbus中,RTU是必选项,ASCII是可选项,也就是说RTU是必须要实现的。 (2)ENTER_CRITICAL_SECTION( )和EXIT_CRITICAL_SECTION( ) 这两个函数是进入和退出关键进程,这里定义为__disable_irq()和__enable_irq(),后边两个函数是ARM内核函数,作用是禁止中断和使能中断。 #define ENTER_CRITICAL_SECTION( ) __disable_irq() #define EXIT_CRITICAL_SECTION( ) __enable_irq() 2.3协议栈初始化 在main.c文件中的while(1)之前,调用eMBInit( )完成协议栈的初始化,然后调用eMBEnable( )使能协议栈。eMBInit( )的定义如下:eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ),共5个参数,eMode模式:RTU或ASCII,ucSlaveAddress从站地址,ucPort单片机的串口号,ulBaudRate波特率,eParity校验方式,我们这里串口参数是在Cube生产的函数里初始化的,而且串口接收或发送的数据HAL库函数已经处理好了,因此,孔丙火(微信公众号:孔丙火)认为,有用的参数只有eMode,ucSlaveAddress,ulBaudRate,其余两个设错了,其实也没影响(仅限于本文的实现方法)。 在modbus_app.c中定义如下全局变量: uint8_t ucUsrSlaveAddress;//从站地址 uint32_t ulUsrBaudRate;//波特率 eMBParity eUsrParity;//校验方式 uint8_t ucUsrStopBits;//停止位 新建modbus_app.h,在其中进行extern定义,一般在其他c文件中使用。 最终,在main.c中做如下调用: eMBInit( MB_RTU, ucUsrSlaveAddress, 1, ulUsrBaudRate, eUsrParity ); 2.4串口参数初始化 在MX_USART2_UART_Init()中修改,此函数是Cube自动生成的,在usart.c中。 最终函数如下: huart2.Instance = USART2; huart2.Init.BaudRate = ulUsrBaudRate; if(eUsrParity == MB_PAR_NONE)huart2.Init.WordLength = UART_WORDLENGTH_8B; else huart2.Init.WordLength = UART_WORDLENGTH_9B; if(ucUsrStopBits == 1)huart2.Init.StopBits = UART_STOPBITS_1; else if(ucUsrStopBits == 2)huart2.Init.StopBits = UART_STOPBITS_2; if(eUsrParity == MB_PAR_NONE)huart2.Init.Parity = UART_PARITY_NONE; else if(eUsrParity == MB_PAR_EVEN)huart2.Init.Parity = UART_PARITY_EVEN; else if(eUsrParity == MB_PAR_ODD)huart2.Init.Parity = UART_PARITY_ODD; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } 波特率使用了全局变量,单片机的数据位个数和停止位根据全局变量确定。 3.总结 这种方法让让串口和Modbus初始化的参数同步起来了,也方便通信参数的在线修改,更接近实战。 ———————————————— 文章在微信公众号同步推出: 孔丙火 ,关注欣赏更多系列文章。-----单片机、ARM、现场总线、PLC、嵌入式软硬件的设计经验分享,秉承“点点滴滴皆智慧”的理念,以实际项目为单元阐述知识点,一起分享,共同交流。
相关资源