本文内容
- HAL库下printf重定向
- 解决问题:Keil下调试可以正常运行,而下载后运行不了
- CAN总线的回环测试,自发自收
printf重定向
实现printf重定向的目的是方便调试,通过UART查看打印的调试信息。
下面以STM32F103C8T6为例:
这些参数需要与串口调试程序约定一致,比如我的,只需要关注红框部分即可:
修改stm32f1xx_hal.c
,添加以下代码:
#include "stdio.h"
extern UART_HandleTypeDef huart1;
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
接收中断
在main.c中添加:
#include <string.h>
#define RXBUFFERSIZE 256
char RxBuffer[RXBUFFERSIZE];
uint8_t aRxBuffer;
uint8_t Uart1_Rx_Cnt = 0;
将下面代码添加到/* USER CODE BEGIN 2 */
处。需要注意位置,必须要写在BEGIN和END之间,否则在通过CubeMX重新生成代码的时候会被删除。
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
这三个参数的含义在头文件中有说明:
@param huart Pointer to a UART_HandleTypeDef structure that containsthe configuration information for the specified UART module.
@param pData Pointer to data buffer (u8 or u16 data elements).
@param Size Amount of data elements (u8 or u16) to be received.
在官方库中,类似的命名的含义是中断处理函数。在执行结束后需要手动清除中断标志位。
在HAL库中,该函数的作用是开启串口1的接收中断,并准备接收一个字节的数据。
HAL库下,在执行完一次中断之后,会自动关闭该中断。如果要保持开启,那么需要在中断回调函数中再次执行HAL_UART_Receive_IT()
当串口1接收到一个字节的数据时,会触发接收中断。中断服务程序会将接收到的数据存入接收缓冲区,并调用用户指定的回调函数。
在/* USER CODE BEGIN 4 */
的部分添加如下代码:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(Uart1_Rx_Cnt >= 255)
{
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer));
HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF);
}
else
{
RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer;
if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D))
{
HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF);
while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);
Uart1_Rx_Cnt = 0;
memset(RxBuffer,0x00,sizeof(RxBuffer));
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);
}
这一部分的功能是,收到串口发来的数据会原文返回,也是为了方便调试。
MicroLIB
我出现的问题是,在keil里调试,可以正常输出,电脑上的串口调试程序可以正常接收。
但下载之后,电脑上的串口调试程序没有任何响应。
解决方式就是勾选“Use MicroLIB”:
单元测试
在main.c的while里定时打印hello
,看串口调试程序能不能收到:
代码必须要写在/* USER CODE BEGIN 3 */
之后!
因为在通过STM32CubeMX重新生成代码的时候,会清除其它部分的代码。
上文提到的修改stm32f1xx_hal.c
默认不会被重置。
CAN内部回环测试
因为我只有一块自带CAN的板子,所以要调试的话,必须要用回环模式:自发自收。
对于CAN的用法,在stm32f1xx_hal_can.c
头部的注释中已经给出:
这些库函数的内容随着bsp的更新可以会做修改。如果版本不一致,函数名和结构体可能也不一样,需要根据.c
文件给出的官方文档修改。
发送数据
CAN在发送之前,需要先执行HAL_CAN_Start(&hcan)
。F103C8T6自带一个CAN,所以CubeMX生成代码的时候有一个hcan变量。如果是其他板子,有多个CAN接口,那么生成的可能是hcan1
、hcan2
。
CAN发送的数据是不定长的,一块板子可能有多个CAN。
所以发送数据的时候,需要指定用哪个CAN接口,发送多长的数据。
各个参数的具体作用,头文件中也已给出,自行翻译即可,不再赘述:
@param hcan pointer to a CAN_HandleTypeDef structure that contains the configuration information for the specified CAN.
@param pHeader pointer to a CAN_TxHeaderTypeDef structure.
@param aData array containing the payload of the Tx frame.
@param pTxMailbox pointer to a variable where the function will return the TxMailbox used to store the Tx message.
This parameter can be a value of @arg CAN_Tx_Mailboxes.
其中,hcan在CubeMX中已经配好,直接&hcan
取地址即可。
pHeader
需要自行设置,我起的变量名叫CAN_TxHeaderTypeDefStructrue
CAN_TxHeaderTypeDef CAN_TxHeaderTypeDefStructrue;
CAN_TxHeaderTypeDefStructrue.StdId=0x123;
CAN_TxHeaderTypeDefStructrue.DLC=4;
CAN_TxHeaderTypeDefStructrue.ExtId=0x123;
CAN_TxHeaderTypeDefStructrue.IDE=CAN_ID_STD;
CAN_TxHeaderTypeDefStructrue.RTR=CAN_RTR_DATA;
这是CAN_TxHeaderTypeDef
结构体的官方注释:
/**
* @brief CAN Tx message header structure definition
*/
typedef struct
{
uint32_t StdId; /*!< Specifies the standard identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x7FF. */
uint32_t ExtId; /*!< Specifies the extended identifier.
This parameter must be a number between Min_Data = 0 and Max_Data = 0x1FFFFFFF. */
uint32_t IDE; /*!< Specifies the type of identifier for the message that will be transmitted.
This parameter can be a value of @ref CAN_identifier_type */
uint32_t RTR; /*!< Specifies the type of frame for the message that will be transmitted.
This parameter can be a value of @ref CAN_remote_transmission_request */
uint32_t DLC; /*!< Specifies the length of the frame that will be transmitted.
This parameter must be a number between Min_Data = 0 and Max_Data = 8. */
FunctionalState TransmitGlobalTime; /*!< Specifies whether the timestamp counter value captured on start
of frame transmission, is sent in DATA6 and DATA7 replacing pData[6] and pData[7].
@note: Time Triggered Communication Mode must be enabled.
@note: DLC must be programmed as 8 bytes, in order these 2 bytes are sent.
This parameter can be set to ENABLE or DISABLE. */
} CAN_TxHeaderTypeDef;
StdId
:标准帧IDDLC
:数据长度,单位为字节ExtId
:扩展帧IDRTR
:远程传输请求,0为数据帧,表示要发送数据IDE
:选择是标准帧还是扩展帧。
标准帧和扩展帧的区别在于帧ID长度不同,扩展帧支持更多的设备挂载。
aData
指向的是要发送的数据数组。
我定义的是:uint8_t data[]={1,2,3,4};
pTxMailbox
指向的变量,将存储,要发送的数据的邮箱。这个变量不需要初始化,作用是以回调的方式存储返回的部分数据。
在F103C8T6中,有三个邮箱,选取哪一个,在HAL库中实现,我们无需关心。
我声明的是:uint32_t pTxMailBox;
这个变量我没有进行初始化,也不需要初始化。会在函数执行结束时自动赋值。
接收数据
CubeMX并没有生成接收数据的代码。
接收到数据的中断也需要手动打开。
在开启之前,需要先配置过滤器。主要是读取约定好的消息格式。
这部分代码CubeMX并没有生成,需要在can.c
下自行实现。
/* USER CODE BEGIN 0 */
void CAN_Filter_Configure(void){
CAN_FilterTypeDef sFilterConfig;
sFilterConfig.FilterActivation=ENABLE;
sFilterConfig.FilterBank=1;
sFilterConfig.FilterFIFOAssignment=CAN_FILTER_FIFO1;
sFilterConfig.FilterIdHigh=0x0000;
sFilterConfig.FilterIdLow=0x0000;
sFilterConfig.FilterMaskIdHigh=0x0000;
sFilterConfig.FilterMaskIdLow=0x0000;
sFilterConfig.FilterMode=CAN_FILTERMODE_IDMASK;
sFilterConfig.FilterScale=CAN_FILTERSCALE_16BIT;
sFilterConfig.SlaveStartFilterBank=17;
if(HAL_CAN_ConfigFilter(&hcan,&sFilterConfig)!=HAL_OK)
Error_Handler();
}
uint8_t rxbuf[8];
/* USER CODE END 0 */
通过HAL_CAN_ActivateNotification
开启中断。
HAL_CAN_ActivateNotification(&hcan,CAN_IT_RX_FIFO1_MSG_PENDING);
该方法不同于UART处的HAL_UART_Receive_IT
。不需要在CAN的中断处理函数中再次执行。
为了验证CAN通讯,在中断处理函数中打印字符串"can"
,也是写在can.c中:
/* USER CODE BEGIN 1 */
void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan){
CAN_RxHeaderTypeDef CAN_RxHeader;
if (HAL_CAN_GetRxMessage(hcan,CAN_RX_FIFO1,&CAN_RxHeader,rxbuf) != HAL_OK)
Error_Handler();
else
printf("can");
}
/* USER CODE END 1 */
单元测试
在while循环中每隔0.5s
向CAN总线发送一次数据。
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
HAL_CAN_AddTxMessage(&hcan,&CAN_TxHeaderTypeDefStructrue,data,&pTxMailBox);
HAL_Delay(500);
/* USER CODE BEGIN 3 */
}
在CubeMX中配置的是回环模式,发送的数据会被自己接收,执行中断处理函数。
中断处理函数的内容已在上文实现,会向串口发送"can"
字符串。
实验现象
也可以在keil中调试:
rxbuf
的内容就是循环中往CAN总线上发送的{1,2,3,4}
Demo代码
参考
- https://blog.csdn.net/as480133937/article/details/99073783
- https://blog.csdn.net/qq_49520586/article/details/122745967
- https://wlink.blog.csdn.net/article/details/116206252
- https://blog.csdn.net/chen18221987993/article/details/109156084
- https://doc.embedfire.com/mcu/stm32/h750prov/hal/zh/latest/book/CAN.html