目录
3. 复用推挽输出(Alternate Function Push-Pull Output)
4. 复用开漏输出(Alternate Function Open-Drain Output)
HAL_UART_IRQHandler(&huart1);(自动生成)
Clock No Stretch Mode(时钟无拉伸模式)
Primary Address Length selection(主地址长度选择):
基础设置
在sys里Serial Wire
在RCC中设置晶振
设置时钟
时钟树:
时钟树系统介绍
以STM32F103C8T6为参考
具象化时钟树:
LSE,LSI,HSE,HSI:
LSE,HSE是芯片内部RC振荡电路
配置流程:
1.需要外部晶振先在RCC中配置
2.配置系统时钟
clock configuration
选择HSE和PLLCLK
对定时器的计算(配置)很重
GPIO:
在这里选择GPIO的形式
配置GPIO的类型的具体东西(上拉,下拉。上升延触发中断,下降沿触发中断)
User label一般不会管的
Output:
1. 推挽输出(Push-Pull Output)
- 特点:可以输出高、低电平,通常连接数字器件。推挽结构一般是指两个三极管(或MOSFET)分别受两个互补信号的控制,总是在一个三极管导通的时候另一个截止
2. 开漏输出(Open-Drain Output)
- 特点:输出端相当于三极管的集电极(或MOSFET的漏极),要得到高电平状态需要外部上拉电阻。在没有外部上拉电阻的情况下,只能输出低电平。
3. 复用推挽输出(Alternate Function Push-Pull Output)
- 特点:当GPIO端口被配置为复用功能时(如用作I2C的SCL、SDA等),可以配置为复用推挽输出模式
4. 复用开漏输出(Alternate Function Open-Drain Output)
- 特点:与开漏输出类似,但用于复用功能。在需要外部上拉电阻来定义高电平的复用功能场合,可以配置为复用开漏输出模式。
上拉下拉取决于原理图是否有其电阻
Input:
上拉输入(Pull-Up Input)
- 特点:在这种模式下,GPIO端口的输入引脚通过一个上拉电阻连接到高电平(通常是VCC)。当外部没有将该输入引脚拉向低电平时,引脚将保持在高电平状态。
浮空输入(Floating Input)
- 特点:在这种模式下,GPIO端口的输入引脚既不接高电平也不接低电平,即处于“浮空”状态。此时,引脚的电平状态完全由外部输入决定,如果引脚悬空(即无外部信号输入),则电平状态不确定
下拉输入(Pull-Down Input)
- 特点:与上拉输入相反,下拉输入模式下,GPIO端口的输入引脚通过一个下拉电阻连接到低电平(通常是GND)。当外部没有将该输入引脚拉向高电平时,引脚将保持在低电平状态。
模拟输入(Analog Input)
- 特点:模拟输入模式下,GPIO端口用于接收连续的模拟信号(如声音、光线等),并通过模数转换器(ADC)等外部元器件将这些模拟信号转换为数字信号进行处理。
GPIO详细知识点讲解:
STM32HAL-GPIO基础理论以及函数应用_hal gpio-优快云博客
GPIO相关函数解析(HAL库)_hal库gpio函数-优快云博客
///
///
中断:
中断知识:
详细讲解:详细讲述STM32HAL的中断系统和基础函数参数应用-优快云博客
NVIC(嵌套向量中断控制器):
STM32 的中断向量具有两个属性,一个为抢占属性(抢占优先级),另一个为响应属性(子优先级),其属性编号越小,表明它的优先级别越高
stm32的抢占优先级和响应优先级(也叫子优先级)_stm32中断抢占优先级和响应优先级-优快云博客
中断函数(以USART为例):
图片来自于:STM32 HAL库学习-USART 接收中断 回调函数_stm32中断接收数据帧,接收回调函数-优快云博客
USART1_IRQHandler(void)(自动生成)
在HAL(硬件抽象层)库中,
USART_UX_IRQHandler
的命名模式通常用于表示USART(通用同步/异步接收/发送器)某个特定实例(由 "X" 代表,其中 "X" 是具体的USART编号,如1、2、3等)的中断服务例程(Interrupt Service Routine, ISR)。然而,需要注意的是,在标准的HAL库实现中,可能不会直接使用USART_UX_IRQHandler
这样的命名,而是使用更具体的命名,如USART1_IRQHandler
、USART2_IRQHandler
等,这些命名直接对应了STM32微控制器的具体USART实例。但是,从概念上讲,
USART_UX_IRQHandler
可以被视为一个模板或占位符,用于表示USART中断服务例程的通用形式。在这个上下文中,“U” 可能是一个占位符,用于表示USART这一大类设备,而“X”则是一个变量,用于指示具体的USART实例编号。在HAL库中,
USART_UX_IRQHandler
(或其实例化的具体形式,如USART1_IRQHandler
)的主要职责是:
- 响应USART设备的中断请求。
- 读取中断源的状态,以确定中断的类型(如接收完成、发送完成、错误等)。
- 调用HAL库提供的适当函数来处理中断事件。这些函数可能是直接处理中断的底层函数,也可能是触发用户定义的回调函数的包装器。
- 清除中断标志,以确保中断不会被重复触发。
然而,在实际应用中,开发者通常不需要直接编写这些ISR,因为STM32CubeMX等工具可以自动生成它们,并将它们与HAL库中的相应处理函数关联起来。开发者需要关注的是配置USART外设以产生所需的中断,以及实现和注册中断回调函数来执行特定的中断处理逻辑。
如果确实需要手动编写或修改
USART_UX_IRQHandler
(或其具体实例),那么开发者应该遵循STM32的参考手册和HAL库的文档,以确保正确地处理中断并避免潜在的错误。
生成在stm32f1xx_it.c
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/* STM32F1xx外围中断处理程序*/
/*在这里为使用的外设添加中断处理程序。* /
对于可用的外设中断处理程序名称,*/
/*请参考启动文件(startup_stm32f1xx.s)。
/******************************************************************************/
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
HAL_UART_IRQHandler(&huart1);(自动生成)
代码只是参考,没必要一段一段看
在STM32的HAL库中,HAL_UART_IRQHandler()
函数并不是直接用来被用户代码调用的,而是作为UART(通用异步收发传输器)中断服务例程(ISR)的一部分,由NVIC(嵌套向量中断控制器)在UART中断发生时自动调用
/**
* @brief This function handles UART interrupt request.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
/ * *
这个函数处理UART中断请求。
指向UART_HandleTypeDef结构体的指针,该结构体包含
*指定UART模块的配置信息。
* @retval无
* /
*/
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
uint32_t isrflags = READ_REG(huart->Instance->SR);
uint32_t cr1its = READ_REG(huart->Instance->CR1);
uint32_t cr3its = READ_REG(huart->Instance->CR3);
uint32_t errorflags = 0x00U;
uint32_t dmarequest = 0x00U;
/* If no error occurs */
errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
if (errorflags == RESET)
{
/* UART in mode Receiver -------------------------------------------------*/
if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
}
/* If some errors occur */
if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET)
|| ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
{
/* UART parity error interrupt occurred ----------------------------------*/
if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_PE;
}
/* UART noise error interrupt occurred -----------------------------------*/
if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_NE;
}
/* UART frame error interrupt occurred -----------------------------------*/
if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
{
huart->ErrorCode |= HAL_UART_ERROR_FE;
}
/* UART Over-Run interrupt occurred --------------------------------------*/
if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET)
|| ((cr3its & USART_CR3_EIE) != RESET)))
{
huart->ErrorCode |= HAL_UART_ERROR_ORE;
}
/* Call UART Error Call back function if need be --------------------------*/
if (huart->ErrorCode != HAL_UART_ERROR_NONE)
{
/* UART in mode Receiver -----------------------------------------------*/
if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
}
/* If Overrun error occurs, or if any error occurs in DMA mode reception,
consider error as blocking */
dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
{
/* Blocking error : transfer is aborted
Set the UART state ready to be able to start again the process,
Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
UART_EndRxTransfer(huart);
/* Disable the UART DMA Rx request if enabled */
if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
{
ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
/* Abort the UART DMA Rx channel */
if (huart->hdmarx != NULL)
{
/* Set the UART DMA Abort callback :
will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
{
/* Call Directly XferAbortCallback function in case of error */
huart->hdmarx->XferAbortCallback(huart->hdmarx);
}
}
else
{
/* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered error callback*/
huart->ErrorCallback(huart);
#else
/*Call legacy weak error callback*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
}
else
{
/* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered error callback*/
huart->ErrorCallback(huart);
#else
/*Call legacy weak error callback*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
}
else
{
/* Non Blocking error : transfer could go on.
Error is notified to user through user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered error callback*/
huart->ErrorCallback(huart);
#else
/*Call legacy weak error callback*/
HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
huart->ErrorCode = HAL_UART_ERROR_NONE;
}
}
return;
} /* End if some error occurs */
/* Check current reception Mode :
If Reception till IDLE event has been selected : */
if ((huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
&& ((isrflags & USART_SR_IDLE) != 0U)
&& ((cr1its & USART_SR_IDLE) != 0U))
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
/* Check if DMA mode is enabled in UART */
if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
{
/* DMA mode enabled */
/* Check received length : If all expected data are received, do nothing,
(DMA cplt callback will be called).
Otherwise, if at least one data has already been received, IDLE event is to be notified to user */
uint16_t nb_remaining_rx_data = (uint16_t) __HAL_DMA_GET_COUNTER(huart->hdmarx);
if ((nb_remaining_rx_data > 0U)
&& (nb_remaining_rx_data < huart->RxXferSize))
{
/* Reception is not complete */
huart->RxXferCount = nb_remaining_rx_data;
/* In Normal mode, end DMA xfer and HAL UART Rx process*/
if (huart->hdmarx->Init.Mode != DMA_CIRCULAR)
{
/* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */
ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);
ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Disable the DMA transfer for the receiver request by resetting the DMAR bit
in the UART CR3 register */
ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
/* At end of Rx process, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;
ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
/* Last bytes received, so no need as the abort is immediate */
(void)HAL_DMA_Abort(huart->hdmarx);
}
/* Initialize type of RxEvent that correspond to RxEvent callback execution;
In this case, Rx Event type is Idle Event */
huart->RxEventType = HAL_UART_RXEVENT_IDLE;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered Rx Event callback*/
huart->RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#else
/*Call legacy weak Rx Event callback*/
HAL_UARTEx_RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
return;
}
else
{
/* DMA mode not enabled */
/* Check received length : If all expected data are received, do nothing.
Otherwise, if at least one data has already been received, IDLE event is to be notified to user */
uint16_t nb_rx_data = huart->RxXferSize - huart->RxXferCount;
if ((huart->RxXferCount > 0U)
&& (nb_rx_data > 0U))
{
/* Disable the UART Parity Error Interrupt and RXNE interrupts */
ATOMIC_CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;
ATOMIC_CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
/* Initialize type of RxEvent that correspond to RxEvent callback execution;
In this case, Rx Event type is Idle Event */
huart->RxEventType = HAL_UART_RXEVENT_IDLE;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
/*Call registered Rx complete callback*/
huart->RxEventCallback(huart, nb_rx_data);
#else
/*Call legacy weak Rx Event callback*/
HAL_UARTEx_RxEventCallback(huart, nb_rx_data);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
}
return;
}
}
/* UART in mode Transmitter ------------------------------------------------*/
if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
{
UART_Transmit_IT(huart);
return;
}
/* UART in mode Transmitter end --------------------------------------------*/
if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
{
UART_EndTransmit_IT(huart);
return;
}
}
回调函数(CallBack)
回调函数有多个,在此举出两个最常见的,需要时再学看:
都是虚函数需要自己重定义
/**
* @brief Tx Half Transfer completed callbacks.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
*/
__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function should not be modified, when the callback is needed,
the HAL_UART_TxHalfCpltCallback could be implemented in the user file
*/
}
/**
* @brief Rx Transfer completed callbacks.
* @param huart Pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
*/
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(huart);
/* NOTE: This function should not be modified, when the callback is needed,
the HAL_UART_RxCpltCallback could be implemented in the user file
*/
}
如:
/**
* @brief Rx传输回调函数
* @param huart: UART句柄类型指针
* @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART_UX) /* 如果是串口1 */
{
if((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
{
if(g_usart_rx_sta & 0x4000) /* 接收到了0x0d */
{
if(g_rx_buffer[0] != 0x0a)
{
g_usart_rx_sta = 0; /* 接收错误,重新开始 */
}
else
{
g_usart_rx_sta |= 0x8000; /* 接收完成了 */
}
}
else /* 还没收到0X0D */
{
if(g_rx_buffer[0] == 0x0d)
{
g_usart_rx_sta |= 0x4000;
}
else
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0] ;
g_usart_rx_sta++;
if(g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
}
}
}
}
HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
}
HAL_UART_Receive_IT
此段摘自:HAL库 串口收发函数解析_hal解析串口接收-优快云博客
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Process Locked */
__HAL_LOCK(huart);
huart->pRxBuffPtr = pData;
huart->RxXferSize = Size;
huart->RxXferCount = Size;
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->RxState = HAL_UART_STATE_BUSY_RX;
/* Process Unlocked */
__HAL_UNLOCK(huart);
/* Enable the UART Parity Error Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_PE);
/* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
__HAL_UART_ENABLE_IT(huart, UART_IT_ERR);
/* Enable the UART Data Register not empty Interrupt */
__HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);
return HAL_OK;
}
else
{
return HAL_BUSY;
}
}
记住!这个函数不是用来接收数据的!这个函数不是用来接收数据的!这个函数不是用来接收数据的!他是用来打开中断,配置串口中断的!不是真正的接收数据的中断函数,很容易把它和其他两个函数混淆。看了上面两个函数的解释,这个函数的内容应该不难理解,这也就是,我们要手动打开串口中断,就要在main函数里面首先写下它,否则无法进入串口中断(亲测如此),其次还要在回调函数里面添加这个函数(因为之前就说过一旦进入回调函数,串口中断就会关闭),为了下一次接收数据考虑,需要这么做
///
///
定时器:
高级定时器内部结构
这部分将分为时基单元,输出比较,输入捕获,从模式控制器来分析讲解
时基单元结构讲解:
预加载:
右上角两图中红线为活跃寄存器,蓝色为影子寄存器
存在的阴影就是影子寄存器
时基单元的基本配置与参数讲解:
时钟树配置好有注意最右侧的两个参数:
APB1 Timer clocks 定时器的RCC频率大小
APB2 Timer clocks 定时器的RCC频率大小
注意定时器属于哪一个分频器(APB1,APB2)
例子:
一个向上计数1ms的定时器中断配置
如上图输入时钟的频率为8MHz,APB1和APB2的时钟频率一样(这两个的区别是一个最大频率为72MHz一个为36MHz),选择TIM1作为实验对象(高级定时器)
Interval clock为8MHz
从上到下参数配置为:7 UP 999 No Division 0 Enable(预频器加载)
在NVIC中选择开启中断,再在NVIC中配置中断的优先级
可以输出一个1ms的定时器
输出比较结构讲解:
定时器有四个通道
输出比较的8种模式:
输出比较的基本配置与参数讲解:
例子:PWM呼吸灯:
Mode:设置了输出比较通道的工作模式。以下是各个模式选项的含义:
Frozen:在这种模式下,输出比较通道不会对任何事件做出反应。无论计数器与比较寄存器值是否匹配,输出引脚的状态都不会改变。这种模式通常用于计时目的,而不需要实际改变输出引脚的状态。
Active Level on match:当计数器的值与比较寄存器值匹配时,通道输出引脚被置为高电平(逻辑“1”)。在这种模式下,计数器值与比较寄存器值匹配时会使输出引脚变为高电平。
Inactive Level on match:当计数器的值与比较寄存器值匹配时,通道输出引脚被置为低电平(逻辑“0”)。在这种模式下,计数器值与比较寄存器值匹配时会使输出引脚变为低电平。
Toggle on match:当计数器的值与比较寄存器值匹配时,通道输出引脚的状态会被翻转(即从高电平变为低电平,或从低电平变为高电平)。这种模式用于生成方波信号,频率由计数器周期和比较寄存器值决定。
Forced Active:输出引脚被强制置为高电平(逻辑“1”),不管计数器值与比较寄存器值的匹配情况。这种模式用于需要持续高电平输出的情况。
Forced Inactive:输出引脚被强制置为低电平(逻辑“0”),不管计数器值与比较寄存器值的匹配情况。这种模式用于需要持续低电平输出的情况。Pulse:脉冲宽度,就是CCR的值
Output compare preload:设置CCR是否使用预装载功能。本示例不动态的修改CCR的值,设置为Enable和Disable无影响。
CH Polarity:通道极性。如果参数Mode设置为Active level on match 或Intactive level on match等与通道极性有关的模式,此参数就是输出的有效电平。本示例模式设置为匹配时输出翻转,此参数无关。
CH Idle State:用于配置输出比较通道在定时器未运行或未使能时的默认电平状态。它确保在计时器停止或不活动期间,输出引脚保持预设的电平,通常为低电平(Reset)或高电平(Set),以防止意外的电平浮动或错误信号干扰,从而提高系统的稳定性和可靠性。
输入捕获结构讲解:
输入捕获的基本配置和参数讲解:
左上角
左下角(时基单元配置看前面内容)
从模式控制器结构讲解:
TI:Timer input--定时器输入
x:代表从哪个通道来的
F:Filter——滤波后的
P:Polarized——极性选择后
y:到哪个通道去
作为从机
作为主机:
Cubemx的从模式控制器配置
从机除开编码器模式下的配置
最上面两个:slave mode:从机 trigger source:TRGI的信号来源
例子:测量占空比:
在slave mode中选择RESET mode ,TRGI的来源为TI1FP1
从机编码器模式下的配置:
在编码器模式下:PSC设置为多少那编码器检测转动了多少次才计数一次,ARR的大小则确定了编码器计数的上限
编码器代码:
///
///
USART配置:
- UART:仅支持异步通信方式。在异步通信中,数据的传输不需要事先建立好同步时钟信号,而是通过起始位、数据位、校验位和停止位等控制信号来实现数据的同步和传输。
- USART:不仅支持异步通信,还支持同步通信方式。在同步通信中,数据的传输需要外部时钟信号的同步,可以通过外部时钟源或主/从模式下使用另一个USART作为主时钟源来实现同步传输。
模式:
GPIO模式:
中断选择:
传输配置
波特率要和另一端相同:如9600
生成的代码参考:
STM32 HAL库函数学习 UART篇_stm32 hal uart-优快云博客
1.
HAL_UART_MspInit
这个函数是UART(或USART)硬件抽象层(HAL)初始化过程中的一部分,用于执行与UART外设相关的底层(MSP:MCU Service Package)初始化操作。具体来说,这个函数负责配置UART的GPIO(通用输入输出)引脚、时钟、NVIC(嵌套向量中断控制器)中断(如果需要的话)、DMA(直接内存访问,如果启用了DMA传输)等硬件级别的设置。
在HAL库中,
HAL_UART_Init
函数会被调用以初始化UART的配置(如波特率、数据位、停止位等),而HAL_UART_MspInit
则需要在HAL_UART_Init
之前或之中被调用,以确保硬件级别的配置已经准备好。2.
HAL_UART_MspDeInit
与
HAL_UART_MspInit
相反,HAL_UART_MspDeInit
函数用于执行UART的底层去初始化(或称为反初始化)操作。这个函数会撤销在HAL_UART_MspInit
中进行的所有配置,包括GPIO引脚的配置、时钟的禁用、NVIC中断的禁用等。这个函数在UART不再需要使用时调用,以释放硬件资源。3.
MX_USART3_UART_Init
这个函数通常是由STM32CubeMX工具自动生成的,用于配置和初始化特定的USART(如USART3)外设。这个函数内部会调用
HAL_UART_Init
来配置USART的通信参数(如波特率、数据位、停止位等),并可能调用HAL_UART_MspInit
来配置与USART相关的硬件资源(尽管在某些情况下,HAL_UART_MspInit
的调用可能会被放在MX_USART3_UART_Init
函数之外的其他位置)。
MX_USART3_UART_Init
的名称可能会根据STM32CubeMX项目的具体配置而有所不同,但其基本目的和功能是类似的:为指定的USART外设提供完整的初始化配置。总结
在STM32的HAL库开发中,
HAL_UART_MspInit
和HAL_UART_MspDeInit
负责UART外设的底层初始化和去初始化,而MX_USART3_UART_Init
(或类似名称的函数)则负责特定USART外设的完整初始化,包括调用HAL_UART_Init
进行配置和(可选地)调用HAL_UART_MspInit
进行硬件资源的配置。
void MX_USART3_UART_Init(void)
{
/* USER CODE BEGIN USART3_Init 0 */
/* USER CODE END USART3_Init 0 */
/* USER CODE BEGIN USART3_Init 1 */
/* USER CODE END USART3_Init 1 */
huart3.Instance = USART3;
huart3.Init.BaudRate = 115200;
huart3.Init.WordLength = UART_WORDLENGTH_8B;
huart3.Init.StopBits = UART_STOPBITS_1;
huart3.Init.Parity = UART_PARITY_NONE;
huart3.Init.Mode = UART_MODE_TX_RX;
huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart3.Init.OverSampling = UART_OVERSAMPLING_16;
huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart3.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart3) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart3, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart3, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart3) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART3_Init 2 */
HAL_UART_Init(&huart3);
HAL_UART_Receive_IT(&huart3,rx_buffer3,1);
/* USER CODE END USART3_Init 2 */
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};
if(uartHandle->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspInit 0 */
/* USER CODE END UART4_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART4;
PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* UART4 clock enable */
__HAL_RCC_UART4_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
/**UART4 GPIO Configuration
PC10 ------> UART4_TX
PC11 ------> UART4_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF8_UART4;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/* UART4 interrupt Init */
HAL_NVIC_SetPriority(UART4_IRQn, 4, 0);
HAL_NVIC_EnableIRQ(UART4_IRQn);
/* USER CODE BEGIN UART4_MspInit 1 */
/* USER CODE END UART4_MspInit 1 */
}
else if(uartHandle->Instance==UART5)
{
/* USER CODE BEGIN UART5_MspInit 0 */
/* USER CODE END UART5_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART5;
PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* UART5 clock enable */
__HAL_RCC_UART5_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**UART5 GPIO Configuration
PB12 ------> UART5_RX
PB13 ------> UART5_TX
*/
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF14_UART5;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* UART5 interrupt Init */
HAL_NVIC_SetPriority(UART5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(UART5_IRQn);
/* USER CODE BEGIN UART5_MspInit 1 */
/* USER CODE END UART5_MspInit 1 */
}
else if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART1;
PeriphClkInitStruct.Usart16ClockSelection = RCC_USART16910CLKSOURCE_D2PCLK2;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 3, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
else if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspInit 0 */
/* USER CODE END USART2_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART2;
PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* USART2 clock enable */
__HAL_RCC_USART2_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
/**USART2 GPIO Configuration
PD5 ------> USART2_TX
PD6 ------> USART2_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* USART2 interrupt Init */
HAL_NVIC_SetPriority(USART2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspInit 1 */
/* USER CODE END USART2_MspInit 1 */
}
else if(uartHandle->Instance==USART3)
{
/* USER CODE BEGIN USART3_MspInit 0 */
/* USER CODE END USART3_MspInit 0 */
/** Initializes the peripherals clock
*/
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USART3;
PeriphClkInitStruct.Usart234578ClockSelection = RCC_USART234578CLKSOURCE_D2PCLK1;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/* USART3 clock enable */
__HAL_RCC_USART3_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* USART3 interrupt Init */
HAL_NVIC_SetPriority(USART3_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspInit 1 */
/* USER CODE END USART3_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==UART4)
{
/* USER CODE BEGIN UART4_MspDeInit 0 */
/* USER CODE END UART4_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_UART4_CLK_DISABLE();
/**UART4 GPIO Configuration
PC10 ------> UART4_TX
PC11 ------> UART4_RX
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_10|GPIO_PIN_11);
/* UART4 interrupt Deinit */
HAL_NVIC_DisableIRQ(UART4_IRQn);
/* USER CODE BEGIN UART4_MspDeInit 1 */
/* USER CODE END UART4_MspDeInit 1 */
}
else if(uartHandle->Instance==UART5)
{
/* USER CODE BEGIN UART5_MspDeInit 0 */
/* USER CODE END UART5_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_UART5_CLK_DISABLE();
/**UART5 GPIO Configuration
PB12 ------> UART5_RX
PB13 ------> UART5_TX
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_12|GPIO_PIN_13);
/* UART5 interrupt Deinit */
HAL_NVIC_DisableIRQ(UART5_IRQn);
/* USER CODE BEGIN UART5_MspDeInit 1 */
/* USER CODE END UART5_MspDeInit 1 */
}
else if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspDeInit 0 */
/* USER CODE END USART1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
/* USART1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspDeInit 1 */
/* USER CODE END USART1_MspDeInit 1 */
}
else if(uartHandle->Instance==USART2)
{
/* USER CODE BEGIN USART2_MspDeInit 0 */
/* USER CODE END USART2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART2_CLK_DISABLE();
/**USART2 GPIO Configuration
PD5 ------> USART2_TX
PD6 ------> USART2_RX
*/
HAL_GPIO_DeInit(GPIOD, GPIO_PIN_5|GPIO_PIN_6);
/* USART2 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART2_IRQn);
/* USER CODE BEGIN USART2_MspDeInit 1 */
/* USER CODE END USART2_MspDeInit 1 */
}
else if(uartHandle->Instance==USART3)
{
/* USER CODE BEGIN USART3_MspDeInit 0 */
/* USER CODE END USART3_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART3_CLK_DISABLE();
/**USART3 GPIO Configuration
PB10 ------> USART3_TX
PB11 ------> USART3_RX
*/
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);
/* USART3 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART3_IRQn);
/* USER CODE BEGIN USART3_MspDeInit 1 */
/* USER CODE END USART3_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
ADC配置:
只有常规序列,没有讲解注入组
ADC基础结构讲解:
cubemx配置讲解:
周期大小配置:
时钟树的最右下角就是ADC的时钟信号
时钟树中的ADC时钟频率不能超过14MHz(默认规定)右下角
如上图,计算出的最佳采样时间为0.85us(通过最佳采样时间公式计算出)乘以ADC的时钟频率得出的,采样时间所占的周期,在加上12位逐次逼近型ADC所固有的转化时间12.5cycle(12cycle+0.5cycle),就能在RANK里
设置其采样总共时间(一共有8个档位)
当计算的采样时间位于8档中间时,选择数值大的越精确,选择数值小的越快
adc的常规序列配置:
每一个RANK都代表了一个常规序列中的格子
RANK中的结构:
通道号加采样时间(总)
上图的外部触发
代码启动函数:
///
///
IIC配置:
硬件IIC:专门电路实现
模拟IIC:用GPIO和软件软件模拟实现
12、I2C主机(HAL库的轮询)_哔哩哔哩_bilibili
不同芯片的范围不一样,具体参考数据手册
IIC Speed Mode
标准模式(Standard Mode, SM):
- 通讯速率:100kbps(即100kbit/s,或12.5KB/s,因为1字节=8比特)。
- 特点:这是I2C总线最基本的通讯速度,适用于较长的总线和较慢的设备,如LCD显示屏、温度传感器等。在标准模式下,时钟频率最高可以达到100 kHz,总线电容最大为400 pF。
- 快速模式(Fast Mode, FM):
- 通讯速率:400kbps(即400kbit/s,或50KB/s)。
- 特点:这种通讯速度适用于数据传输速度要求较高的设备,如EEPROM、实时时钟等。在快速模式下,时钟频率最高可以达到400 kHz,总线电容最大为400 pF。
- 快速模式+(Fast Mode Plus, FM+):
- 通讯速率:1Mbps(即1Mbit/s,或125KB/s)。
- 特点:快速模式加是I2C总线的一种改进版快速模式,适用于对数据传输速度要求更高的设备,如高速ADC、高速数字信号处理器等。
- 高速模式(High Speed Mode, HS):
- 通讯速率:3.4Mbps(即3.4Mbit/s,或435KB/s)。
- 特点:高速模式是I2C总线的一种较快的通讯速度,适用于对数据传输速度要求非常高的设备,如高清视频处理器、高速闪存等。在高速模式下,时钟频率最高可以达到3.4 MHz,总线电容最大为400 pF。
- 超快速模式(Ultra Fast Mode, UFM):
- 通讯速率:5Mbps(即5Mbit/s,或525KB/s)。
- 特点:这种通讯速度适用于需要超高速数据传输的设备,如图像处理器、高速网络接口等。需要注意的是,超快速传输是单向传输且不兼容其他模式。
IIC Clock Speed的范围
- 标准模式(Standard Mode):
- 时钟频率:最高可达100kHz(即100,000Hz)。
- 通讯速率:最高可达100kbps(即100kbit/s),因为每个时钟周期可以传输1bit数据。
- 快速模式(Fast Mode):
- 时钟频率:最高可达400kHz(即400,000Hz)。
- 通讯速率:最高可达400kbps(即400kbit/s)。
- 高速模式(High Speed Mode, HS):
- 时钟频率:最高可达3.4MHz(即3,400,000Hz)。
- 通讯速率:最高可达3.4Mbps(即3.4Mbit/s)。
Clock No Stretch Mode(时钟无拉伸模式)
Clock No Stretch Mode(时钟无拉伸模式)是IIC(Inter-Integrated Circuit,即集成电路总线)通信中的一个重要特性,它涉及到主设备和从设备在数据传输过程中的时钟控制。
基本概念
在IIC通信中,时钟信号(SCL)由主设备产生,用于同步主设备和从设备之间的数据传输。然而,在某些情况下,从设备可能需要更多的时间来准备数据或处理中断,这时从设备可以通过将时钟线(SCL)拉低来延长时钟周期,这就是所谓的“时钟拉伸”(Clock Stretching)。然而,当Clock No Stretch Mode被启用时,从设备将不再具有这种能力,即它们不能通过拉低SCL线来延长时钟周期。
工作原理
标准行为:在默认情况下,许多IIC从设备支持时钟拉伸,这意味着当从设备需要更多时间来处理数据时,它会将SCL线拉低,直到准备好继续传输为止。此时,主设备会检测到SCL线保持低电平,并相应地停止发送数据,直到SCL线再次被拉高。
Clock No Stretch Mode:当启用Clock No Stretch Mode时,从设备不再能够拉低SCL线来延长时钟周期。这意味着主设备将按照预定的时钟频率持续发送数据,而不会等待从设备准备好。这可以提高数据传输的效率,但要求从设备必须能够在主设备发送数据的速率下及时响应。
应用场景
- 高速数据传输:在需要高速数据传输的应用中,启用Clock No Stretch Mode可以减少数据传输的延迟,提高系统的整体性能。
- 从设备性能限制:如果从设备的处理速度较慢,或者其内部逻辑不允许进行时钟拉伸,那么启用Clock No Stretch Mode可以确保数据传输的顺利进行。
注意事项
- 兼容性:在启用Clock No Stretch Mode之前,需要确保所有连接到IIC总线的从设备都支持该模式,否则可能会导致通信失败。
- 系统稳定性:在某些情况下,从设备可能需要通过时钟拉伸来避免数据丢失或错误。因此,在启用Clock No Stretch Mode之前,需要仔细评估系统的稳定性和可靠性。
Primary Address Length selection(主地址长度选择):
IIC知识点详解:
STM32通讯全面概述(持续更新未完成)_串口通信pdata-优快云博客
SPI配置:

上部分:
下部分:
传输格式:
数据位长度与数据传输顺序:
分频系数与波特率:
时钟极性与时钟相位:
理论概念:
鸣谢:
感谢正点原子,小蜜蜂,铁头山羊,江科大录制的视频:
江科大:[1-1] 课程简介_哔哩哔哩_bilibili
铁头山羊:[STM32 HAL库]课程介绍,最佳教程,没有之一~_哔哩哔哩_bilibili
正点原子:【正点原子】手把手教你学STM32 HAL库开发全集【真人出镜】STM32入门教学视频教程 单片机 嵌入式_哔哩哔哩_bilibili