【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笔记

建议直接按照链接里面的步骤修改,第四个可以不管。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值