本帖最后由 Zachary_yo 于 2023-4-1 14:51 编辑
[MCU] HC32F4A0-FreeRTOS移植
经过半天的学习HC32F4A0的CAN功能,终于将它的配置了解了一二。首先,了解一下HC32F4A0的CAN功能,它具有经典CAN和CAN FD两种,其中CAN1只支持经典CAN,CAN2支持经典CAN和CAN FD。以下是CAN1的特性。
本次CAN功能的配置,主要涉及到以下几个方面,CAN时钟设置、IO口复用设置、 过滤器配置、波特率与采样点计算、中断及中断回调函数。
CAN时钟设置,是为了能精确的计算波特率,使得工作更可靠。起初,只设置了FCG时钟,后来看到了手册中的说明,如下图。因此,需要调用相关API设置时钟。
用到的时钟配置代码有 CLK_SetCANClockSrc( CLK_CAN1, CLK_CANCLK_SYSCLK_DIV6 )和FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_CAN1, ENABLE );
IO口复用设置,由下图可知,CAN1_TX和CAN1_RX复用功能为Func_60和Func_61。
过滤器配置:因为测试CAN首发所有帧的能力,因此不过滤ID,以下是过滤器配置代码,将过滤器配置为不过滤,接收所有数据。
/* ---过滤器配置:不过滤任何ID--- */
CANFilter.u32ID = 0x0;
CANFilter.u32IDMask = 0x1FFFFFFF;
CANFilter.u32IDType = CAN_ID_STD_EXT;
波特率与采样点计算。CAN 控制器有两个时钟,控制逻辑时钟和通信时钟。CAN1控制器控制逻辑时钟挂在PCLK1上,初始化完后,PCLK的时钟频率是120MHz;通信时钟需要通过CLK_SetCANClockSrc函数来设置。这里将通信始终设置为CLK_SetCANClockSrc( CLK_CAN1, CLK_CANCLK_SYSCLK_DIV6 ),也就是将通信始终设置为20MHz。设置好始终后,就可以配置波特率了。总结出的波特率计算公式如下:
BaudRate = CANCLK / Prescaler / ( TimeSeg1 + TimeSeg2 )
本程序的波特率设置为BaudRate = 20MHz / 2 / ( 15 + 5 ) = 500kbps。
采样点计算公式如下:
SamplePoint = TimeSeg1 / ( TimeSeg1 + TimeSeg2 )
本程序的采样点设置为SamplePoint = 15 / ( 15 + 5 ) = 75%。
以下是CAN1的详细配置代码,其中还包括了中断的配置,在这里需要注意的是,CAN的中断共用了一个中断向量、共用了一个中断服务函数,这与之前串口中断需要每个中断都配置一个中断向量和中断服务函数是不一样的。不清楚芯片设计人员为什么不统一起来,按主流芯片来说,都是只需要一个中断就可以了,在中断服务函数中区别是什么类型中断。而这个CAN就是共享了一个中断服务函数,与串口中断不同。
/**
************************************* Copyright ******************************
*
* (C) Copyright 2023,Zachary ,HPU, China.
* All Rights Reserved
*
* FileName : bsp_Can.c
* Version : v1.0
* Author : Zachary
* Date : 2023-03-30
* Description:
******************************************************************************
*/
/* --- Includes ----------------------------------------------------------*/
#include "bsp_Can.h"
#include "bsp_TCA9539.h"
#include "os_includes.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
#define CAN_TX_PORT GPIO_PORT_D
#define CAN_TX_PIN GPIO_PIN_05
#define CAN_TX_PIN_FUNC GPIO_FUNC_60
#define CAN_RX_PORT GPIO_PORT_D
#define CAN_RX_PIN GPIO_PIN_04
#define CAN_RX_PIN_FUNC GPIO_FUNC_61
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
extern TaskHandle_t xTaskHandleCan1;
extern QueueHandle_t xQueueHandleCan1Frame;
/* --- Functions ---------------------------------------------------------*/
static void CAN_RxCallback( void );
/*------------------------------------------------------------------------*/
/**************************************************************
* @name bsp_Can_Init
* @brief
* @param None
* @retval
* @author Zachary
* @data 2023-03-30
**************************************************************/
void bsp_Can_Init( void )
{
stc_can_init_t CAN_InitStruct;
stc_can_filter_config_t CANFilter;
stc_irq_signin_config_t CANIRQ;
LL_PERIPH_WE( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
/* ---打开CAN1时钟--- */
FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_CAN1, ENABLE ); /* ---120MHz--- */
CLK_SetCANClockSrc( CLK_CAN1, CLK_CANCLK_SYSCLK_DIV6 ); /* ---CANCLK = 120MHz / 6 = 20Mhz --- */
/* ---设置IO口复用--- */
GPIO_SetFunc( CAN_TX_PORT, CAN_TX_PIN, CAN_TX_PIN_FUNC );
GPIO_SetFunc( CAN_RX_PORT, CAN_RX_PIN, CAN_RX_PIN_FUNC );
/* ---过滤器配置:不过滤任何ID--- */
CANFilter.u32ID = 0x0;
CANFilter.u32IDMask = 0x1FFFFFFF;
CANFilter.u32IDType = CAN_ID_STD_EXT;
/* ---波特率计算公式: BaudRate = CANCLK / Prescaler / ( TimeSeg1 + TimeSeg2 ) = 500kbps--- */
/* ---采样点计算公式: SamplePoint = TimeSeg1 / ( TimeSeg1 + TimeSeg2 ) = 75% */
CAN_StructInit( &CAN_InitStruct );
CAN_InitStruct.stcBitCfg.u32Prescaler = 2U;
CAN_InitStruct.stcBitCfg.u32TimeSeg1 = 15U;
CAN_InitStruct.stcBitCfg.u32TimeSeg2 = 5U;
CAN_InitStruct.stcBitCfg.u32SJW = 5U;
CAN_InitStruct.pstcFilter = &CANFilter;
CAN_InitStruct.u16FilterSelect = CAN_FILTER1;
CAN_InitStruct.u8WorkMode = CAN_WORK_MD_NORMAL;
CAN_Init( CM_CAN1, &CAN_InitStruct );
CAN_IntCmd( CM_CAN1, CAN_INT_ALL, DISABLE );
CAN_IntCmd( CM_CAN1, CAN_INT_RX, ENABLE );// | CAN_INT_RX_BUF_FULL | CAN_INT_RX_OVERRUN | CAN_INT_ERR_INT
/* Can中断向量表配置 */
CANIRQ.enIntSrc = INT_SRC_CAN1_HOST;
CANIRQ.enIRQn = INT004_IRQn;
CANIRQ.pfnCallback = &CAN_RxCallback;
INTC_IrqSignIn( &CANIRQ );
NVIC_ClearPendingIRQ( CANIRQ.enIRQn );
NVIC_SetPriority( CANIRQ.enIRQn, 3 );
NVIC_EnableIRQ( CANIRQ.enIRQn);
/* ---使能CAN收发芯片--- */
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN4, TCA9539_PIN_RESET );
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN4, TCA9539_DIR_OUT );
LL_PERIPH_WP( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
}
/**************************************************************
* @Name CAN_RxCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-30
**************************************************************/
static void CAN_RxCallback( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
stc_can_rx_frame_t Can1_Receive_Frame;
CAN_GetRxFrame( CM_CAN1, &Can1_Receive_Frame );
if( CAN_GetStatus( CM_CAN1, CAN_FLAG_RX ) == SET )
{
if( xTaskHandleCan1 != NULL )
{
/* ---使用队列通知任务处理函数--- */
xQueueSendFromISR( xQueueHandleCan1Frame, &Can1_Receive_Frame, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
CAN_ClearStatus( CM_CAN1, CAN_FLAG_RX );
}
// if( CAN_GetStatus( CM_CAN1, CAN_FLAG_RX_BUF_FULL ) == SET )
// {
// ;
// }
}
本CAN测试是在FreeRTOS下的,在上面的代码中的中断服务函数中使用了队列来传输CAN接收到的数据。因此,我们需要在对应的CAN数据处理函数中,接收数据。在这个任务中,不分析数据,只是将数据再通过CAN发送出去,实现CAN数据的回显,代码如下:
/* ---CAN任务创建函数--- */
xTaskCreate( xTaskCan1,
"xTaskCan1",
512,
NULL,
2,
&xTaskHandleCan1 );
/**************************************************************
* @Name xTaskHandleCan1
* @brief
* @param pvParameters: [ÊäÈë/³ö]
* @retval
* @author Zachary
* @Data 2023-03-31
**************************************************************/
static void xTaskCan1( void *pvParameters )
{
stc_can_tx_frame_t CAN1_TxFrame;
for( ;; )
{
if( pdPASS == xQueueReceive( xQueueHandleCan1Frame, ( stc_can_tx_frame_t * )&CAN1_TxFrame, portMAX_DELAY ) )
{
CAN_FillTxFrame( CM_CAN1, CAN_TX_BUF_STB, &CAN1_TxFrame );
CAN_StartTx( CM_CAN1, CAN_TX_REQ_STB_ALL );
while( SET == CAN_GetStatus( CM_CAN1, CAN_FLAG_TX_GOING ) )
{
;
}
}
}
}
以下是main.c中的代码:
/**
************************************* Copyright ******************************
*
* (C) Copyright 2022,Zachary ,HPU, China.
* All Rights Reserved
*
* FileName : main.c
* Version : v1.0
* Author : Zachary
* Date : 2023-02-13
* Description: This is a template project of HC32F4A0
---Zachary,in Zhejiang Hanpu.
******************************************************************************
*/
/* Includes --------------------------------------------------------------*/
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "bsp_Gpio.h"
#include "bsp_TCA9539.h"
#include "bsp_NT35510.h"
#include "bsp_XPT2046.h"
#include "bsp_AT24Cxx.h"
#include "bsp_Can.h"
#include "os_includes.h"
#include "lvgl.h"
#include "lv_port_disp_template.h"
#include "lv_port_indev_template.h"
#include "./demos/widgets/lv_demo_widgets.h"
#include "./demos/music/lv_demo_music.h"
#include "./demos/benchmark/lv_demo_benchmark.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
stc_clock_freq_t sysclk;
static TaskHandle_t xTaskHandleStart = NULL;
static TaskHandle_t xTaskHandleTask1 = NULL;
static TaskHandle_t xTaskHandleTask2 = NULL;
TaskHandle_t xTaskHandleCan1 = NULL;
QueueHandle_t xQueueHandleCan1Frame = NULL;
struct netconn *tcpconn, *newconn;
/* --- Functions ---------------------------------------------------------*/
static void SysInit( void );
static void bsp_SetSysClk( void );
static void xTaskStart( void *pvParameters );
static void xTask1( void *pvParameters );
static void xTask2( void *pvParameters );
static void xTaskCan1( void *pvParameters );
/*------------------------------------------------------------------------*/
/**************************************************************
* @Name xTaskHandleCan1
* @brief
* @param pvParameters: [ÊäÈë/³ö]
* @retval
* @author Zachary
* @Data 2023-03-31
**************************************************************/
static void xTaskCan1( void *pvParameters )
{
stc_can_tx_frame_t CAN1_TxFrame;
for( ;; )
{
if( pdPASS == xQueueReceive( xQueueHandleCan1Frame, ( stc_can_tx_frame_t * )&CAN1_TxFrame, portMAX_DELAY ) )
{
CAN_FillTxFrame( CM_CAN1, CAN_TX_BUF_STB, &CAN1_TxFrame );
CAN_StartTx( CM_CAN1, CAN_TX_REQ_STB_ALL );
while( SET == CAN_GetStatus( CM_CAN1, CAN_FLAG_TX_GOING ) )
{
;
}
}
}
}
/**************************************************************
* @Name xTask1
* @brief
* @param pvParameters: [ÊäÈë/³ö]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask1( void *pvParameters )
{
// lv_init();
// lv_port_disp_init();
// lv_port_indev_init();
// lv_demo_widgets();
// lv_demo_music();
// lv_demo_keypad_encoder();
// lv_demo_stress();
for( ;; )
{
// lv_task_handler();
vTaskDelay( 5 );
}
}
/**************************************************************
* @Name xTask2
* @brief
* @param pvParameters: [ÊäÈë/³ö]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask2( void *pvParameters )
{
for( ;; )
{
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
vTaskDelay( 250 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
vTaskDelay( 250 );
}
}
/**************************************************************
* @Name xTaskStart
* @brief
* @param pvParameters: [ÊäÈë/³ö]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTaskStart( void *pvParameters )
{
xQueueHandleCan1Frame = xQueueCreate( 10, sizeof( stc_can_rx_frame_t ) );
xTaskCreate( xTask1, /* ---ÈÎÎñº¯Êý--- */
"xTask1", /* ---ÈÎÎñÃû³Æ--- */
1024, /* ---ÈÎÎñ¶ÑÕ»--- */
NULL, /* ---´«Èë²ÎÊý--- */
8, /* ---ÈÎÎñÓÅÏȼ¶--- */
&xTaskHandleTask1 ); /* ---ÈÎÎñ¾ä±ú--- */
xTaskCreate( xTask2, /* ---ÈÎÎñº¯Êý--- */
"xTask2", /* ---ÈÎÎñÃû³Æ--- */
1024, /* ---ÈÎÎñ¶ÑÕ»--- */
NULL, /* ---´«Èë²ÎÊý--- */
1, /* ---ÈÎÎñÓÅÏȼ¶--- */
&xTaskHandleTask2 ); /* ---ÈÎÎñ¾ä±ú--- */
xTaskCreate( xTaskCan1, /* ---ÈÎÎñº¯Êý--- */
"xTaskCan1", /* ---ÈÎÎñÃû³Æ--- */
512, /* ---ÈÎÎñ¶ÑÕ»--- */
NULL, /* ---´«Èë²ÎÊý--- */
2, /* ---ÈÎÎñÓÅÏȼ¶--- */
&xTaskHandleCan1 ); /* ---ÈÎÎñ¾ä±ú--- */
for( ;; )
{
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN6, TCA9539_PIN_SET );
vTaskDelay( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN6, TCA9539_PIN_RESET );
vTaskDelay( 500 );
}
}
/**************************************************************
* @Name StartTaskCreate
* @brief ´´½¨¿ªÊ¼ÈÎÎñ
* @param None
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
void StartTaskCreate( void )
{
xTaskCreate( xTaskStart, /* ---ÈÎÎñº¯Êý--- */
"xTaskStart", /* ---ÈÎÎñÃû³Æ--- */
1024, /* ---ÈÎÎñ¶ÑÕ»--- */
NULL, /* ---´«Èë²ÎÊý--- */
0, /* ---ÈÎÎñÓÅÏȼ¶--- */
&xTaskHandleStart ); /* ---ÈÎÎñ¾ä±ú--- */
}
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-22
**************************************************************/
int32_t main( void )
{
SysInit();
StartTaskCreate();
vTaskStartScheduler();
while( 1 )
{
;
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
NVIC_SetPriority ( HardFault_IRQn, (1UL << 2) - 1UL ); /* set Priority for Systick Interrupt */
NVIC_EnableIRQ( HardFault_IRQn );
CLK_GetClockFreq( &sysclk ); /* ---¼ì²éʱÖÓÊÇ·ñÕýÈ·--- */
delay_init( 240 ); /* ---ϵͳµÎ´ð¶¨Ê±Æ÷³õʼ»¯--- */
bsp_Gpio_Init(); /* ---³õʼ»¯Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN5, TCA9539_DIR_OUT );
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN6, TCA9539_DIR_OUT );
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
bsp_Can_Init();
}
/**************************************************************
* @Name SetSysClk
* @brief ²Î¿¼×Ô¹Ù·½¹¤³Ì
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void bsp_SetSysClk( void )
{
stc_clock_xtal_init_t stcXtalInit;
stc_clock_pll_init_t stcPLLHInit;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM ); /* ---½â±£»¤Ïà¹ØÍâÉè--- */
/* PCLK0, HCLK Max 240MHz */
/* PCLK1, PCLK4 Max 120MHz */
/* PCLK2, PCLK3 Max 60MHz */
/* EX BUS Max 120MHz */
CLK_SetClockDiv( CLK_BUS_CLK_ALL, \
( CLK_PCLK0_DIV1 | CLK_PCLK1_DIV2 | CLK_PCLK2_DIV4 | \
CLK_PCLK3_DIV4 | CLK_PCLK4_DIV2 | CLK_EXCLK_DIV2 | \
CLK_HCLK_DIV1 ) ); /* ---ÍâÉè×ÜÏß·ÖƵ--- */
CLK_XtalStructInit( &stcXtalInit );
/* Config Xtal and enable Xtal */
stcXtalInit.u8Mode = CLK_XTAL_MD_OSC; /* ---Ñ¡ÔñÍⲿ¾§Õñ--- */
stcXtalInit.u8Drv = CLK_XTAL_DRV_ULOW; /* ---¾§ÕñÇý¶¯ÄÜÁ¦ ÓÐÔ´¾§ÕñÇý¶¯ÄÜÁ¦Ç¿--- */
stcXtalInit.u8State = CLK_XTAL_ON;
stcXtalInit.u8StableTime = CLK_XTAL_STB_2MS; /* ---¾§ÕñÎȶ¨µÈ´ýÖÜÆÚ--- */
CLK_XtalInit( &stcXtalInit );
CLK_PLLStructInit( &stcPLLHInit ); /* ---ËøÏà»·¼Ä´æÆ÷³õʼ»¯--- */
/* VCO = (8/1)*120 = 960MHz*/
stcPLLHInit.u8PLLState = CLK_PLL_ON;
stcPLLHInit.PLLCFGR = 0UL;
stcPLLHInit.PLLCFGR_f.PLLM = 1UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLN = 120UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLP = 4UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLQ = 4UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLR = 4UL - 1UL;
stcPLLHInit.PLLCFGR_f.PLLSRC = CLK_PLL_SRC_XTAL;
CLK_PLLInit( &stcPLLHInit );
/* Highspeed SRAM set to 0 Read/Write wait cycle */
SRAM_SetWaitCycle( SRAM_SRAMH, SRAM_WAIT_CYCLE0, SRAM_WAIT_CYCLE0 );
/* SRAM1_2_3_4_backup set to 1 Read/Write wait cycle */
SRAM_SetWaitCycle( ( SRAM_SRAM123 | SRAM_SRAM4 | SRAM_SRAMB ), SRAM_WAIT_CYCLE1, SRAM_WAIT_CYCLE1 );
/* 0-wait @ 40MHz */
EFM_SetWaitCycle( EFM_WAIT_CYCLE5 );
/* 4 cycles for 200 ~ 250MHz */
GPIO_SetReadWaitCycle( GPIO_RD_WAIT4 );
CLK_SetSysClockSrc( CLK_SYSCLK_SRC_PLL );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM ); /* ---±£»¤Ïà¹ØÍâÉè--- */
}
/**************************************************************
* @Name HardFault_Handler
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-27
**************************************************************/
void HardFault_Handler( void )
{
while( 1 )
{
;
}
}
以下是HC32F4A0的CAN接口与CAN调试器的连接图。
以下是测试图片:
测试视频:
WeChat_20230401143829
工程代码:
|