【STM32】移植HAL库代码至GD32平台,并成功调通CAN总线收发例程
一、移植对象
从 STM32F103CBT6 移植到 GD32F303CCT6 。
二、can总线通信源码
1. mian.c
#include "main.h"
#include "can.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "delay.h"
extern CAN_TxPacketTypeDef g_CanTxPacket;
void SystemClock_Config(void);
uint32_t a;
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_CAN_Init();
MX_USART2_UART_Init();
Delay_Init();
CAN_Init();
printf("----------------------------------------\r\n");
CAN_SetTxPacket();
while (1)
{
if(CAN_Transmit(&g_CanTxPacket) != 0)
printf("failed\r\n");
Delay_ms(1000);
a++; //调试变量,可删除
}
}
2. uart.c
#include "usart.h"
#include "stdio.h"
//定义最大接收字节数 200,可根据需求调整
#define UART_REC_LEN 200
//串口接收缓存(1字节)
uint8_t buf = 0;
// 接收缓冲, 串口接收到的数据放在这个数组里,最大UART_REC_LEN个字节
uint8_t UART_RX_Buffer[UART_REC_LEN];
// 接收状态
// bit15, 接收完成标志
// bit14, 接收到0x0d
// bit13~0, 接收到的有效字节数目
uint16_t UART_RX_STA=0;
int fputc(int ch, FILE *f)
{
unsigned char temp[1]={ch};
HAL_UART_Transmit(&huart2,temp,1,0xffff);
return ch;
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 判断中断是由哪个串口触发的
if(huart->Instance == USART2)
{
// 判断接收是否完成(UART_RX_STA bit15 位是否为1)
if((UART_RX_STA & 0x8000) == 0)
{
// 如果已经收到了 0x0d (回车),
if(UART_RX_STA & 0x4000)
{
// 则接着判断是否收到 0x0a (换行)
if(buf == 0x0a)
// 如果 0x0a 和 0x0d 都收到,则将 bit15 位置为1
UART_RX_STA |= 0x8000;
else
// 否则认为接收错误,重新开始
UART_RX_STA = 0;
}
else // 如果没有收到了 0x0d (回车)
{
//则先判断收到的这个字符是否是 0x0d (回车)
if(buf == 0x0d)
{
// 是的话则将 bit14 位置为1
UART_RX_STA |= 0x4000;
}
else
{
// 否则将接收到的数据保存在缓存数组里
UART_RX_Buffer[UART_RX_STA & 0X3FFF] = buf;
UART_RX_STA++;// 如果接收数据大于UART_REC_LEN(200字节),则重新开始接收
if(UART_RX_STA > UART_REC_LEN - 1)
UART_RX_STA = 0;
}
}
}
// 重新开启中断
HAL_UART_Receive_IT(&huart2, &buf, 1);
}
}
3. can.c
#include "can.h"
#include "stdio.h"
#define CAN_BASE_ID 0 ///< CAN标准ID,最大11位,也就是0x7FF
#define CAN_FILTER_MODE_MASK_ENABLE 1 ///< CAN过滤器模式选择:=0:列表模式 =1:屏蔽模式
#define CAN_ID_TYPE_STD_ENABLE 1 ///< CAN过滤ID类型选择:=1:标准ID,=0:扩展ID
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef sFilterConfig;
CAN_FilterRegTypeDef IDH = {0};
CAN_FilterRegTypeDef IDL = {0};
#if CAN_ID_TYPE_STD_ENABLE
IDH.Sub.STID = (CAN_BASE_ID >> 16) & 0xFFFF; // 标准ID高16位
IDL.Sub.STID = (CAN_BASE_ID & 0xFFFF); // 标准ID低16位
#else
IDH.Sub.EXID = (CAN_BASE_ID >> 16) & 0xFFFF; // 扩展ID高16位
IDL.Sub.EXID = (CAN_BASE_ID & 0xFFFF); // 扩展ID低16位
IDL.Sub.IDE = 1; // 扩展帧标志位置位
#endif
sFilterConfig.FilterBank = 0; // 设置过滤器组编号
#if CAN_FILTER_MODE_MASK_ENABLE
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // 屏蔽位模式
#else
sFilterConfig.FilterMode = CAN_FILTERMODE_IDLIST; // 列表模式
#endif
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // 32位宽
sFilterConfig.FilterIdHigh = IDH.value; // 标识符寄存器一ID高十六位,放入扩展帧位
sFilterConfig.FilterIdLow = IDL.value; // 标识符寄存器一ID低十六位,放入扩展帧位
sFilterConfig.FilterMaskIdHigh = IDH.value; // 标识符寄存器二ID高十六位,放入扩展帧位
sFilterConfig.FilterMaskIdLow = IDL.value; // 标识符寄存器二ID低十六位,放入扩展帧位
sFilterConfig.FilterFIFOAssignment = CAN_RX_FIFO0; // 过滤器组关联到FIFO0
sFilterConfig.FilterActivation = ENABLE; // 激活过滤器
sFilterConfig.SlaveStartFilterBank = 14; // 设置从CAN的起始过滤器编号,本单片机只有一个CAN,顾此参数无效
if (HAL_CAN_ConfigFilter(&hcan, &sFilterConfig) != HAL_OK)
{
Error_Handler();
}
}
uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet)
{
if(HAL_CAN_AddTxMessage(&hcan, &packet->hdr, packet->payload, &packet->mailbox) != HAL_OK)
return 1;
return 0;
}
void CAN_Init(void)
{
MX_CAN_Init();
CAN_Filter_Config();
HAL_CAN_Start(&hcan);
HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING); // 使能CAN接收中断
}
CAN_TxPacketTypeDef g_CanTxPacket;
void CAN_SetTxPacket(void)
{
g_CanTxPacket.hdr.StdId = 0x321; // 标准ID
// g_CanTxPacket.hdr.ExtId = 0x10F01234; // 扩展ID
// g_CanTxPacket.hdr.IDE = CAN_ID_STD; // 标准ID类型
g_CanTxPacket.hdr.IDE = CAN_ID_EXT; // 扩展ID类型
g_CanTxPacket.hdr.DLC = 8; // 数据长度
g_CanTxPacket.hdr.RTR = CAN_RTR_DATA; // 数据帧
// g_CanTxPacket.hdr.RTR = CAN_RTR_REMOTE; // 远程帧
g_CanTxPacket.hdr.TransmitGlobalTime = DISABLE;
for(int i = 0; i < 8; i++)
{
g_CanTxPacket.payload[i] = i;
}
}
CAN_HandleTypeDef hcan;
void MX_CAN_Init(void)
{
}
void HAL_CAN_MspInit(CAN_HandleTypeDef* canHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(canHandle->Instance==CAN1)
{
__HAL_RCC_CAN1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
CLEAR_BIT(canHandle->Instance->MCR, CAN_MCR_SLEEP);
}
}
void HAL_CAN_MspDeInit(CAN_HandleTypeDef* canHandle)
{
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *canHandle)
{
static CAN_RxPacketTypeDef packet;
// CAN数据接收
if (canHandle->Instance == hcan.Instance)
{
if (HAL_CAN_GetRxMessage(canHandle, CAN_RX_FIFO0, &packet.hdr, packet.payload) == HAL_OK) // 获得接收到的数据头和数据
{
printf("\r\n\r\n\r\n################### CAN RECV ###################\r\n");
printf("STID:0x%X\r\n",packet.hdr.StdId);
printf("EXID:0x%X\r\n",packet.hdr.ExtId);
printf("DLC :%d\r\n", packet.hdr.DLC);
printf("DATA:");
for(int i = 0; i < packet.hdr.DLC; i++)
{
printf("0x%02X ", packet.payload[i]);
}
HAL_CAN_ActivateNotification(canHandle, CAN_IT_RX_FIFO0_MSG_PENDING); // 再次使能FIFO0接收中断
}
}
}
4. can.h
#include "main.h"
extern CAN_HandleTypeDef hcan;
typedef struct
{
uint32_t mailbox;
CAN_TxHeaderTypeDef hdr;
uint8_t payload[8];
}CAN_TxPacketTypeDef;
typedef struct
{
CAN_RxHeaderTypeDef hdr;
uint8_t payload[8];
}CAN_RxPacketTypeDef;
// CAN过滤器寄存器位宽类型定义
typedef union
{
__IO uint32_t value;
struct
{
uint8_t REV : 1; ///< [0] :未使用
uint8_t RTR : 1; ///< [1] : RTR(数据帧或远程帧标志位)
uint8_t IDE : 1; ///< [2] : IDE(标准帧或扩展帧标志位)
uint32_t EXID : 18; ///< [21:3] : 存放扩展帧ID
uint16_t STID : 11; ///< [31:22]: 存放标准帧ID
} Sub;
} CAN_FilterRegTypeDef;
void MX_CAN_Init(void);
void CAN_Init(void);
void CAN_Filter_Config(void);
void CAN_SetTxPacket(void);
uint8_t CAN_Transmit(CAN_TxPacketTypeDef* packet);
三、实现Delay()函数
由于 GD32调用 HAL_Delay() 函数,会出现延时误差;因此我们自己实现 Delay 延时。
1. delay.c
#include "delay.h"
uint8_t fac_us=0;
uint16_t fac_ms=0;
void Delay_Init()
{
//只可以选择不分频或者8分频,这里选择系统时钟8分频,最后频率为9MHZ
SysTick->CTRL &= ~(1<<2);
//SystemCoreClock为72000000,最终fac_us为9,也就是记录震动9次。因为频率为9MHZ所以为1us
fac_us = SystemCoreClock / 8000000;
fac_ms = fac_us*1000; //1000us=1ms
}
/*
CTRL SysTick控制及状态寄存器
LOAD SysTick重装载数值寄存器
VAL SysTick当前数值寄存器
*/
void Delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD =nus*fac_us; //设置加载的值,比如1us就要计数9次。nus传入1,CALIB=1*9=9,最后就是1us
SysTick->VAL =0x00; //清空计数器中的值,LOAD里的值不是写入后就会加载,而是在systick使能且VAL值为0时才加载
SysTick->CTRL |=SysTick_CTRL_ENABLE_Msk; //使能时钟,开始计时
do
{
temp=SysTick->CTRL; //查询是否计数完成
}while((temp&0x01)&&!(temp&(1<<16))); //先判断定时器是否在运行,再判断是否计数完成
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
void Delay_ms(uint32_t nms)
{
uint32_t temp;
SysTick->LOAD =nms*fac_ms; //设置加载的值,比如1us就要计数9次。nus传入1,CALIB=1*9=9,最后就是1us
SysTick->VAL =0x00; //清空计数器中的值,LOAD里的值不是写入后就会加载,而是在systick使能且VAL值为0时才加载
SysTick->CTRL |=SysTick_CTRL_ENABLE_Msk; //使能时钟,开始计时
do
{
temp=SysTick->CTRL; //查询是否计数完成
}while((temp&0x01)&&!(temp&(1<<16))); //先判断定时器是否在运行,再判断是否计数完成
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
2. delay.h
#ifndef __delay_H
#define __delay_H
#include "stm32f1xx.h" // 相当于51单片机中的 #include <reg51.h>
void Delay_Init(void);
void Delay_us(uint32_t nus);
void Delay_ms(uint32_t nms);
#endif
四、调试问题
1. 修改启动时间 HSE_STARTUP_TIMEOUT 宏定义;改成最大值 999U 。
2. 常见调试报错问题(附带解决方法)
stm32f105移植到gd32f305笔记
建议直接按照链接里面的步骤修改,第四个可以不管。