- 2023-07-11
-
回复了主题帖:
stm32f2+DP83848 lwip socket例程
遇到问题,下载学习一下
-
发表了主题帖:
LwIP Socket编程read/recv读取问题
各位大佬,有没有遇到过LwIP使用socket创建的tcp服务器中read/recv函数单次最大只能读取1460字节数据的问题。
假设我发送2000个字节,那么通过仿真发现,他先收到了1460字节的数据,剩下的字节会在读取一次。
rd_num = read( clientfd2, tcp_svr2_rd, 4096 );
// rd_num = recv( clientfd2, tcp_svr2_rd, 4096, 0 );
大家有碰到过这个问题吗?
- 2023-07-06
-
回复了主题帖:
GD32F305RCT6在线升级
移植一个Ymodem,单片机flash分两个区,在线升级先存到备份区,再复制到主区,复制完成后跳转。
- 2023-04-02
-
回复了主题帖:
HC32F4A0-经典CAN功能测试
秦天qintian0303 发表于 2023-4-2 08:32
CAN功能如何测试比较好,需要特定的调试器?有没有那种USB转CAN的设备
可以测试CAN的回环模式,就是自发自收,这样就不用调试器。
有USB转CAN的。
- 2023-04-01
-
回复了主题帖:
HC32F4A0-经典CAN功能测试
lugl4313820 发表于 2023-4-1 19:50
大佬这个CAN调试器花了不少钱吧?没有仔细看过手册,这个还支持CANFD,比较强呀!
不要钱,公司的
-
发表了主题帖:
HC32F4A0-经典CAN功能测试
本帖最后由 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调试器的连接图。
以下是测试图片:
测试视频:
[localvideo]2b55fdea49e5502bea4d0bf56bd88e42[/localvideo]
工程代码:
- 2023-03-30
-
发表了主题帖:
HC32F4A0-FreeRTOS下LVGL的移植
HC32F4A0-TFTLCD - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
HC32F4A0-硬件SPI应用-触摸屏 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
HC32F4A0-FreeRTOS移植 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
以上三篇是这次移植LVGL的基础,使用的版本是LVGL v8.3。
首先,首先在工程中创建Middleware/LVGL/Src、Middleware/LVGL/Demos虚拟文件夹,再将工程中的LVGL v8.3源码添加到工程中。以下是LVGL源码目录,需要添加的只有src文件夹和demos文件夹。
在src文件夹中,以下文件箭头指示的文件夹中的所有.c文件,是我们需要添加上的,其中draw文件夹中,还需要添加sw子文件夹中的.c文件。另外,还有两个重要的接口文件,lv_port_disp_template.c和lv_port_indev_template.c,分别是LVGL显示接口文件和输入设备(触摸、键盘等)接口文件,这两个文件在..\Middlewares\LittleVGL\examples\porting文件夹中
添加完源码,我们需要添加头文件路径,LVGL的头文件包含都是以路径形式的,如#include "./demos/music/lv_demo_music.h",所以没有必要每个路径都添加进去,只需要添加外层的几个头文件路径即可,如下图所示。
在添加完以上内容后,开始正式的移植工作。打开lv_conf.h,先将顶部的#if 0改为#if 1使能这个文件,再根据屏幕实际分辨率大小,更改MY_DISP_HOR_RES 和 MY_DISP_VER_RES;找到宏定义 LV_TICK_CUSTOM,改为1,并将下方宏定义改为#define LV_TICK_CUSTOM_INCLUDE "os_includes.h"和#define LV_TICK_CUSTOM_SYS_TIME_EXPR (xTaskGetTickCount())。如下图所示。
由于后面需要使用到他的官方demo做演示,因此,将这个文件中的#define LV_USE_DEMO_MUSIC也设为1,启用lv_demo_music,如下图所示。
修改lv_port_disp_template.c接口文件,如果是网上下载的源码,那么,整个文件是不使能的,将文件顶部的#if 0改为#if 1即可。然后,这个文件中需要对这几个函数做更改,分别是void lv_port_disp_init(void)、static void disp_init(void)、static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p),以下是更改内容:
修改后的 lv_port_disp_template.c文件内容如下:
/**
* [url=home.php?mod=space&uid=1307177]@File[/url] lv_port_disp_templ.c
*
*/
/*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_disp_template.h"
#include <stdbool.h>
#include "bsp_NT35510.h"
/*********************
* DEFINES
*********************/
#ifndef MY_DISP_HOR_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
#define MY_DISP_HOR_RES 320
#endif
#ifndef MY_DISP_VER_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
#define MY_DISP_VER_RES 240
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void disp_init(void);
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_disp_init(void)
{
disp_init();
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 240]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 240); /*Initialize the display buffer*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set the resolution of the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
/*You code here*/
bsp_NT35510_Init();
}
volatile bool disp_flush_enabled = true;
/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL
*/
void disp_enable_update(void)
{
disp_flush_enabled = true;
}
/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL
*/
void disp_disable_update(void)
{
disp_flush_enabled = false;
}
extern void SSD_LCD_Color_Fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t *color);
/*Flush the content of the internal buffer the specific area on the display
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
LCD_Color_Fill( area->x1, area->y1, area->x2, area->y2, ( uint16_t * )color_p );
}
lv_disp_flush_ready(disp_drv);
}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
修改lv_port_indev_template.c接口文件,如果是网上下载的源码,那么,整个文件是不使能的,将文件顶部的#if 0改为#if 1即可。这个接口文件包括了所有的输入设备的初始化和映射,如触摸屏、键盘、鼠标等。这里只用到了触摸屏,因此,可以将除了触摸屏相关的都删除。以下是更改好的文件:
/**
* @file lv_port_indev_templ.c
*
*/
/*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_indev_template.h"
#include "../../lvgl.h"
#include "bsp_XPT2046.h"
static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
static void mouse_init(void);
static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool mouse_is_pressed(void);
static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);
static void keypad_init(void);
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static uint32_t keypad_get_key(void);
static void encoder_init(void);
static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static void encoder_handler(void);
static void button_init(void);
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static int8_t button_get_pressed_id(void);
static bool button_is_pressed(uint8_t id);
/**********************
* STATIC VARIABLES
**********************/
lv_indev_t * indev_touchpad;
lv_indev_t * indev_mouse;
lv_indev_t * indev_keypad;
lv_indev_t * indev_encoder;
lv_indev_t * indev_button;
static int32_t encoder_diff;
static lv_indev_state_t encoder_state;
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_indev_init(void)
{
static lv_indev_drv_t indev_drv;
/*------------------
* Touchpad
* -----------------*/
/*初始化触摸屏*/
touchpad_init();
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*------------------
* Touchpad
* -----------------*/
/*Initialize your touchpad*/
static void touchpad_init(void)
{
/*Your code comes here*/
bsp_XPT2046_Init();/* 调用触摸屏初始化函数 */
}
/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*Save the pressed coordinates and the state*/
if(touchpad_is_pressed()) {
touchpad_get_xy(&last_x, &last_y);
data->state = LV_INDEV_STATE_PR;
}
else {
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
data->point.x = last_x;
data->point.y = last_y;
}
/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
tp_dev.scan( 0 );
if( tp_dev.sta & TP_PRES_DOWN )
{
return true;
}
return false;
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
(*x) = tp_dev.x[0];
(*y) = tp_dev.y[0];
}
/*------------------
* Mouse
* -----------------*/
/*Initialize your mouse*/
static void mouse_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the mouse*/
static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
/*Get the current x and y coordinates*/
mouse_get_xy(&data->point.x, &data->point.y);
/*Get whether the mouse button is pressed or released*/
if(mouse_is_pressed()) {
data->state = LV_INDEV_STATE_PR;
}
else {
data->state = LV_INDEV_STATE_REL;
}
}
/*Return true is the mouse button is pressed*/
static bool mouse_is_pressed(void)
{
/*Your code comes here*/
return false;
}
/*Get the x and y coordinates if the mouse is pressed*/
static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
(*x) = 0;
(*y) = 0;
}
/*------------------
* Keypad
* -----------------*/
/*Initialize your keypad*/
static void keypad_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the mouse*/
static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static uint32_t last_key = 0;
/*Get the current x and y coordinates*/
mouse_get_xy(&data->point.x, &data->point.y);
/*Get whether the a key is pressed and save the pressed key*/
uint32_t act_key = keypad_get_key();
if(act_key != 0) {
data->state = LV_INDEV_STATE_PR;
/*Translate the keys to LVGL control characters according to your key definitions*/
switch(act_key) {
case 1:
act_key = LV_KEY_NEXT;
break;
case 2:
act_key = LV_KEY_PREV;
break;
case 3:
act_key = LV_KEY_LEFT;
break;
case 4:
act_key = LV_KEY_RIGHT;
break;
case 5:
act_key = LV_KEY_ENTER;
break;
}
last_key = act_key;
}
else {
data->state = LV_INDEV_STATE_REL;
}
data->key = last_key;
}
/*Get the currently being pressed key. 0 if no key is pressed*/
static uint32_t keypad_get_key(void)
{
/*Your code comes here*/
return 0;
}
/*------------------
* Encoder
* -----------------*/
/*Initialize your keypad*/
static void encoder_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the encoder*/
static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
data->enc_diff = encoder_diff;
data->state = encoder_state;
}
/*Call this function in an interrupt to process encoder events (turn, press)*/
static void encoder_handler(void)
{
/*Your code comes here*/
encoder_diff += 0;
encoder_state = LV_INDEV_STATE_REL;
}
/*------------------
* Button
* -----------------*/
/*Initialize your buttons*/
static void button_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the button*/
static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static uint8_t last_btn = 0;
/*Get the pressed button's ID*/
int8_t btn_act = button_get_pressed_id();
if(btn_act >= 0) {
data->state = LV_INDEV_STATE_PR;
last_btn = btn_act;
}
else {
data->state = LV_INDEV_STATE_REL;
}
/*Save the last pressed button's ID*/
data->btn_id = last_btn;
}
/*Get ID (0, 1, 2 ..) of the pressed button*/
static int8_t button_get_pressed_id(void)
{
uint8_t i;
/*Check to buttons see which is being pressed (assume there are 2 buttons)*/
for(i = 0; i < 2; i++) {
/*Return the pressed button's ID*/
if(button_is_pressed(i)) {
return i;
}
}
/*No button pressed*/
return -1;
}
/*Test if `id` button is pressed or not*/
static bool button_is_pressed(uint8_t id)
{
/*Your code comes here*/
return false;
}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
最后,是LVGL的初始化,顺序是先lv_init() ---> lv_port_disp_init() ---> lv_port_indev_init(),这些我们都在之前创建的xTask1中创建。在这里要说明一个问题,通常多任务情况下,显示的优先级不会是最高的,但是在本程序中,我提高了显示任务的优先级。原因是,显示、eeprom、触摸屏和LED灯都用到了TCA9539的IO口,由于我互斥关系还没有做,当LED闪烁任务优先级高,LVGL显示任务优先级低时,触摸屏片选在执行时,会被高优先级的LED闪烁打断,从而倒是I2C卡死,所以会导致在运行过程中卡死。由于,只是测试,就不花时间来做这个互斥了。解决方法是,屏蔽LED闪烁,或者提高LVGL任务的优先级。以下是xTask1中的内容:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] xTask1
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param pvParameters: [输入/出]
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 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();/* LVGL周期性处理函数 */
vTaskDelay( 5 );
}
}
以下是视频验证:
[localvideo]a1135f324c15686fb011d473c0d0e196[/localvideo]
以下是工程源码:
- 2023-03-28
-
发表了主题帖:
HC32F4A0-LwIP应用之串口服务器
HC32F4A0_FreeRTOS_LwIP移植 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
本篇使用HC32F4A0的串口1和LwIP协议栈实现串口服务器的功能。已经实现了TCP回显服务器,那么在这个基础上添加串口相关代码,使数据能双向透传,实现简易串口服务器的功能。
工作流程分为两块,一个是串口到网络,另一个是网络到串口。这里使用串口1中断接收数据,通过netconn_write API传输给TCP客户端;TCP客户端发送数据到服务器,接收到数据后,通过串口发送出去。理清思路,下面开始实现。
首先实现网络到串口。先创建一个TCP服务器( sys_thread_new( "Uart_Server", xTaskUARTServer, NULL, 512, 4 ) ),建立新的连接后,进入网络数据接收环节,当服务器有数据进来时,我们调用写好的串口中断发送函数,将数据发送出去,我们在串口端就可以接收到相关数据。以下是服务器代码,串口发送函数,将在后面的串口内容中说明。
/* ---创建Tcp串口服务器--- */
sys_thread_new( "Uart_Server", xTaskUARTServer, NULL, 512, 4 );
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] xTaskUARTServer
* [url=home.php?mod=space&uid=159083]@brief[/url] 串口服务器
* @param pvParameters: [输入/出]
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-03-28
**************************************************************/
static void xTaskUARTServer( void *pvParameters )
{
err_t err = ERR_OK;
struct netbuf *recvbuf;
void *data = NULL;
uint16_t len = 0;
tcpconnUart = netconn_new( NETCONN_TCP );
netconn_bind( tcpconnUart, IPADDR_ANY, 5001 );
netconn_listen( tcpconnUart );
for( ;; )
{
err = netconn_accept( tcpconnUart, &newconnUart );
COM1_Info.RxCnt = 0;
if( err == ERR_OK )
{
for( ;; )
{
if( ( err = netconn_recv( newconnUart, &recvbuf ) ) == ERR_OK )
{
do
{
netbuf_data( recvbuf, &data, &len ); /* 搬运收到的数据 */
USART1_SendData_UseIT( data, len ); /* 串口中断发送数据 */
} while( netbuf_next( recvbuf ) >= 0 );
netbuf_delete( recvbuf );
COM1_Info.RxCnt = 0;
}
else
{
netconn_close( newconnUart );
while( ERR_OK != netconn_delete( newconnUart ) )
{
vTaskDelay( 1 );
}
break;
}
}
}
vTaskDelay( 10 );
}
}
接着实现串口到网络。基本思路是串口通过中断接收数据,接收完成后,通过LwIP相关函数(netconn_write),发送出去,在TCP客户端可以接收到发送的数据。以往,我都是通过串口空闲中断来判断一帧数据的接收完成。但是,写程序时发现,HC32F4A0这个单片机没有串口空闲中断这个功能,取而代之的是一个叫串口接收超时中断的功能。在这里忍不住想要吐槽两句,这个串口空闲中断的功能怎么能没有呢,接收超时功能只有4个串口(1、2、6、7)可以用,而且要用到相关定时器0。接收超时功能与定时器的对应情况,如下图所示。
以下是定时器0的配置代码,这里面的超时时间,根据不同的时钟源、分频系数和比较直来设置。这个定时器初始化函数,在串口初始化函数中调用。
/**************************************************************
* @Name bsp_TimerForUSART1_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
static void bsp_TimerForUSART1_Init( void )
{
stc_tmr0_init_t stcTmr0_InitStruct;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
FCG_Fcg2PeriphClockCmd( FCG2_PERIPH_TMR0_1, ENABLE );
TMR0_DeInit( CM_TMR0_1 );
TMR0_SetCountValue( CM_TMR0_1, TMR0_CH_A, 0 );
stcTmr0_InitStruct.u32Func = TMR0_FUNC_CMP;
stcTmr0_InitStruct.u32ClockSrc = TMR0_CLK_SRC_INTERN_CLK; /* ---PLCK1:120MHz--- */
stcTmr0_InitStruct.u32ClockDiv = TMR0_CLK_DIV32;
stcTmr0_InitStruct.u16CompareValue = 330;
TMR0_Init( CM_TMR0_1, TMR0_CH_A, &stcTmr0_InitStruct );
TMR0_HWStartCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
TMR0_HWClearCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
}
下面是串口初始化函数,基本参数初始化后,配置了3个中断,分别是接收中断、发送中断和超时接收中断。
/**************************************************************
* @Name bsp_Usart_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
void bsp_Usart_Init( void )
{
stc_usart_uart_init_t Usart_InitStruct;
stc_irq_signin_config_t stcIrqSigninConfig;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---解保护--- */
/* Configure USART RX/TX pin. */
GPIO_SetFunc( USART1_RX_PORT, USART1_RX_PIN, GPIO_FUNC_33 );
GPIO_SetFunc( USART1_TX_PORT, USART1_TX_PIN, GPIO_FUNC_32 );
USART1_FCG_ENABLE(); /* ---使能串口1功能时钟--- */
USART_UART_StructInit(&Usart_InitStruct);
Usart_InitStruct.u32ClockSrc = USART_CLK_SRC_INTERNCLK; /* ---选择内部时钟--- */
Usart_InitStruct.u32ClockDiv = USART_CLK_DIV64; /* ---时钟64分频--- */
Usart_InitStruct.u32CKOutput = USART_CK_OUTPUT_DISABLE; /* ---不输出时钟--- */
Usart_InitStruct.u32Baudrate = 115200UL;
Usart_InitStruct.u32Parity = USART_PARITY_NONE;
Usart_InitStruct.u32DataWidth = USART_DATA_WIDTH_8BIT;
Usart_InitStruct.u32StopBit = USART_STOPBIT_1BIT;
Usart_InitStruct.u32FirstBit = USART_FIRST_BIT_LSB; /* ---LSB线性--- */
Usart_InitStruct.u32HWFlowControl = USART_HW_FLOWCTRL_NONE;
Usart_InitStruct.u32StartBitPolarity = USART_START_BIT_LOW; /* ---起始位为低--- */
Usart_InitStruct.u32OverSampleBit = USART_OVER_SAMPLE_8BIT; /* ---8倍过采样--- */
if ( LL_OK != USART_UART_Init( CM_USART1, &Usart_InitStruct, NULL ) )
{
for (;;)
{
}
}
stcIrqSigninConfig.enIRQn = INT003_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_RxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT001_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_TI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_TxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT002_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RTO; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_IdleCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
USART_FuncCmd( CM_USART1, ( USART_TX | USART_RX | USART_INT_RX ), ENABLE ); /* ---打开发送接收功能、中断接收功能--- */
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
USART_FuncCmd( CM_USART1, USART_INT_RX_TIMEOUT | USART_RX_TIMEOUT, ENABLE );
COM1_Info.pRxBuf = RxBuf;
COM1_Info.pTxBuf = TxBuf;
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---上保护--- */
bsp_TimerForUSART1_Init();
}
下面是bsp_Usart.h、bsp_Usart.c中的内容:
/**
************************************* Copyright ******************************
* (C) Copyright 2022,Zachray,HPU, China.
* All Rights Reserved
*
* FileName : bsp_Usart.h
* Version : v1.0
* Author : Zachary
* Date : 2023-02-15
* Description: This is the first edition
---Zachary,in Zhejiang Hanpu.
******************************************************************************
*/
#ifndef __BSP_USART_H_
#define __BSP_USART_H_
/* Includes --------------------------------------------------------------*/
#include "main.h"
/* --- Define ------------------------------------------------------------*/
#define USART1_FCG_ENABLE() FCG_Fcg3PeriphClockCmd( FCG3_PERIPH_USART1, ENABLE )
#define USART1_TX_PIN GPIO_PIN_15
#define USART1_TX_PORT GPIO_PORT_H
#define USART1_RX_PIN GPIO_PIN_13
#define USART1_RX_PORT GPIO_PORT_H
/* --- Typedef -----------------------------------------------------------*/
typedef struct
{
uint8_t TxLen;
uint8_t TxCnt;
uint8_t *pTxBuf;
uint8_t RxLen;
uint8_t RxCnt;
uint8_t *pRxBuf;
} COM_Typedef;
/* --- Variables ---------------------------------------------------------*/
extern COM_Typedef COM1_Info;
/* --- Functions ---------------------------------------------------------*/
void bsp_Usart_Init( void );
void USART1_SendData_UseIT( uint8_t *pBuf, uint8_t pLen );
/*------------------------------------------------------------------------*/
#endif /* __BSP_USART_H_ */
/**
************************************* Copyright ******************************
*
* (C) Copyright 2022,Zachary ,HPU, China.
* All Rights Reserved
*
* FileName : bsp_Usart.c
* Version : v1.0
* Author : Zachary
* Date : 2023-02-13
* Description:
******************************************************************************
*/
/* Includes --------------------------------------------------------------*/
#include <stdio.h>
#include "bsp_Usart.h"
#include "delay.h"
#include "os_includes.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
uint8_t TxBuf[250];
uint8_t RxBuf[250];
COM_Typedef COM1_Info;
/* --- Functions ---------------------------------------------------------*/
static void USART1_RxCpltCallback( void );
static void USART1_TxCpltCallback( void );
static void USART1_IdleCallback( void );
static void bsp_TimerForUSART1_Init( void );
/*------------------------------------------------------------------------*/
/**************************************************************
* @Name bsp_Usart_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
void bsp_Usart_Init( void )
{
stc_usart_uart_init_t Usart_InitStruct;
stc_irq_signin_config_t stcIrqSigninConfig;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---解保护--- */
/* Configure USART RX/TX pin. */
GPIO_SetFunc( USART1_RX_PORT, USART1_RX_PIN, GPIO_FUNC_33 );
GPIO_SetFunc( USART1_TX_PORT, USART1_TX_PIN, GPIO_FUNC_32 );
USART1_FCG_ENABLE(); /* ---使能串口1功能时钟--- */
USART_UART_StructInit(&Usart_InitStruct);
Usart_InitStruct.u32ClockSrc = USART_CLK_SRC_INTERNCLK; /* ---选择内部时钟--- */
Usart_InitStruct.u32ClockDiv = USART_CLK_DIV64; /* ---时钟64分频--- */
Usart_InitStruct.u32CKOutput = USART_CK_OUTPUT_DISABLE; /* ---不输出时钟--- */
Usart_InitStruct.u32Baudrate = 115200UL;
Usart_InitStruct.u32Parity = USART_PARITY_NONE;
Usart_InitStruct.u32DataWidth = USART_DATA_WIDTH_8BIT;
Usart_InitStruct.u32StopBit = USART_STOPBIT_1BIT;
Usart_InitStruct.u32FirstBit = USART_FIRST_BIT_LSB; /* ---LSB线性--- */
Usart_InitStruct.u32HWFlowControl = USART_HW_FLOWCTRL_NONE;
Usart_InitStruct.u32StartBitPolarity = USART_START_BIT_LOW; /* ---起始位为低--- */
Usart_InitStruct.u32OverSampleBit = USART_OVER_SAMPLE_8BIT; /* ---8倍过采样--- */
if ( LL_OK != USART_UART_Init( CM_USART1, &Usart_InitStruct, NULL ) )
{
for (;;)
{
}
}
stcIrqSigninConfig.enIRQn = INT003_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_RxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT001_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_TI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_TxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT002_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RTO; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_IdleCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 2UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
USART_FuncCmd( CM_USART1, ( USART_TX | USART_RX | USART_INT_RX ), ENABLE ); /* ---打开发送接收功能、中断接收功能--- */
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
USART_FuncCmd( CM_USART1, USART_INT_RX_TIMEOUT | USART_RX_TIMEOUT, ENABLE );
COM1_Info.pRxBuf = RxBuf;
COM1_Info.pTxBuf = TxBuf;
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---上保护--- */
bsp_TimerForUSART1_Init();
}
/**************************************************************
* @Name USART1_IdleCallback
* @brief 超时功能用作空闲中断
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
extern TaskHandle_t xTaskHandleTask2;
static void USART1_IdleCallback( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( SET == USART_GetStatus( CM_USART1, USART_FLAG_RX_TIMEOUT ) )
{
if( xTaskHandleTask2 != NULL )
{
vTaskNotifyGiveFromISR( xTaskHandleTask2, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
TMR0_Stop( CM_TMR0_1, TMR0_CH_A );
USART_ClearStatus( CM_USART1, USART_FLAG_RX_TIMEOUT );
}
}
/**************************************************************
* @Name USART1_RxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-20
**************************************************************/
static void USART1_RxCpltCallback( void )
{
uint8_t u8Data = 0x0;
if( USART_GetStatus( CM_USART1, USART_FLAG_RX_FULL ) != RESET )
{
u8Data = ( uint8_t )USART_ReadData( CM_USART1 );
COM1_Info.pRxBuf[COM1_Info.RxCnt] = u8Data;
//USART_WriteData( CM_USART1, COM1_Info.pRxBuf[COM1_Info.RxCnt] );
COM1_Info.RxCnt++;
}
}
/**************************************************************
* @Name USART1_TxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-21
**************************************************************/
static void USART1_TxCpltCallback( void )
{
if( USART_GetStatus( CM_USART1, USART_FLAG_TX_EMPTY ) != RESET )
{
if( COM1_Info.TxLen > 0x00 )
{
CM_USART1->DR = COM1_Info.pTxBuf[COM1_Info.TxCnt] & 0X01FF;
COM1_Info.TxCnt++;
COM1_Info.TxLen--;
}
else
{
COM1_Info.TxLen = 0x00;
COM1_Info.TxCnt = 0x00;
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
}
}
}
/**************************************************************
* @Name USART1_SendData_UseIT
* @brief
* @param pBuf: [输入/出]
** pLen: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-23
**************************************************************/
void USART1_SendData_UseIT( uint8_t *pBuf, uint8_t pLen )
{
COM1_Info.TxCnt = 0x00;
COM1_Info.TxLen = pLen;
memcpy( COM1_Info.pTxBuf, pBuf, COM1_Info.TxLen );
CM_USART1->DR = pBuf[COM1_Info.TxCnt];
COM1_Info.TxCnt++;
COM1_Info.TxLen--;
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, ENABLE );
}
/**************************************************************
* @Name bsp_TimerForUSART1_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
static void bsp_TimerForUSART1_Init( void )
{
stc_tmr0_init_t stcTmr0_InitStruct;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
FCG_Fcg2PeriphClockCmd( FCG2_PERIPH_TMR0_1, ENABLE );
TMR0_DeInit( CM_TMR0_1 );
TMR0_SetCountValue( CM_TMR0_1, TMR0_CH_A, 0 );
stcTmr0_InitStruct.u32Func = TMR0_FUNC_CMP;
stcTmr0_InitStruct.u32ClockSrc = TMR0_CLK_SRC_INTERN_CLK; /* ---PLCK1:120MHz--- */
stcTmr0_InitStruct.u32ClockDiv = TMR0_CLK_DIV32;
stcTmr0_InitStruct.u16CompareValue = 330;
TMR0_Init( CM_TMR0_1, TMR0_CH_A, &stcTmr0_InitStruct );
TMR0_HWStartCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
TMR0_HWClearCondCmd( CM_TMR0_1, TMR0_CH_A, ENABLE );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC );
}
/**************************************************************
* @Name _sys_exit
* @brief 定义_sys_exit()以避免使用半主机模式
* @param
* @retval
* @author Zachary
* @Data 2022-09-05
**************************************************************/
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit( int x )
{
x = x;
}
/**************************************************************
* @Name fputc
* @brief
* @param ch: [输入/出]
** f: [输入/出]
* @retval
* @author Zachary
* @Data 2022-02-23
**************************************************************/
int fputc( int ch, FILE *f )
{
USART_WriteData( CM_USART1, ch );
while( USART_GetStatus( CM_USART1, USART_FLAG_TX_EMPTY ) == RESET );
return ch;
}
实现了串口超时接收的功能,在超时接收中断服务函数中调用FreeRTOS的直接通知任务功能,通知串口数据转发处理任务。在这个任务中调用netconn_write函数,将数据通过网络发出去。以下是超时接收中断函数内容、串口数据转发处理任务的内容。需要注意的是,超时接收中断需要软件清零。
/**************************************************************
* @Name USART1_IdleCallback
* @brief 超时功能用作空闲中断
* @param None
* @retval
* @author Zachary
* @Data 2023-03-28
**************************************************************/
extern TaskHandle_t xTaskHandleTask2;
static void USART1_IdleCallback( void )
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if( SET == USART_GetStatus( CM_USART1, USART_FLAG_RX_TIMEOUT ) )
{
if( xTaskHandleTask2 != NULL )
{
vTaskNotifyGiveFromISR( xTaskHandleTask2, &xHigherPriorityTaskWoken );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
TMR0_Stop( CM_TMR0_1, TMR0_CH_A );
USART_ClearStatus( CM_USART1, USART_FLAG_RX_TIMEOUT );
}
}
/**************************************************************
* @Name xTask2
* @brief 用作串口接收转发
* @param pvParameters: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask2( void *pvParameters )
{
for( ;; )
{
ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
if( newconnUart != NULL )
{
netconn_write( newconnUart, COM1_Info.pRxBuf, COM1_Info.RxCnt, NETCONN_COPY );
}
COM1_Info.RxCnt = 0;
}
}
视频验证:
[localvideo]e4b584f4e22696796b5374ad70cb0445[/localvideo]
工程源码:
- 2023-03-27
-
发表了主题帖:
HC32F4A0-LwIP应用之TCP串口服务器
HC32F4A0_FreeRTOS_LwIP移植 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
上篇,已经移植了LwIP,并且成功Ping通。本篇,我们开始运用LwIP的Netconn API接口,最终实现TCP串口服务器。
首先,我们先实现TCP回显服务器。功能如下,在HC32F4A0所在的板子上创建TCP服务器,PC机使用网络调试助手,发送任意数据,服务器回送该数据。由于,前两篇已经实现FreeRTOS和LwIP的移植,现在直接创建TCP服务器任务和编写回显服务器。
先使用struct netconn结构体,创建全局指针变量,*tcpconn和*newconn,tcpconn是用来创建新的连接,连接类型可以为TCP或UDP,这里我们选TCP。newconn用来存放侦听到的新的连接。总体步骤为 创建连接-->绑定端口-->进入侦听状态-->等待新的连接。新的TCP客户端连接上后,进入一个循环,在这个循环中,我们以阻塞的方式等待数据,当有数据时,调用netbuf_data函数将数据取出来,进行相应的操作。以下是TCP回显服务器的代码:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] xTaskTcpServer
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param pvParameters: [输入/出]
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-03-27
**************************************************************/
uint8_t vlist = 0x0;
static void xTaskTcpServer( void *pvParameters )
{
err_t err = ERR_OK;
struct netbuf *recvbuf;
void *data = NULL;
uint16_t len = 0;
tcpconn = netconn_new( NETCONN_TCP );
netconn_bind( tcpconn, IPADDR_ANY, 5000 );
netconn_listen( tcpconn );
for( ;; )
{
err = netconn_accept( tcpconn, &newconn );
char *DispChar = NULL;
LCD_ShowString( 10, 370, lcddev.width, lcddev.height, 24, ( uint8_t * )"Tcp Client is Connected...", WHITE, BLACK );
LCD_ShowString( 10, 400, lcddev.width, lcddev.height, 24, ( uint8_t * )"Receive:", WHITE, BLACK );
if( err == ERR_OK )
{
for( ;; )
{
if( ( err = netconn_recv( newconn, &recvbuf ) ) == ERR_OK )
{
do
{
netbuf_data( recvbuf, &data, &len );
netconn_write( newconn, data, len, NETCONN_COPY );
DispChar = ( char * )malloc( len );
memcpy( DispChar, data, len );
if( vlist >= 5 )
{
vlist = 0;
LCD_ShowString( 106, 400 + 0 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 1 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 2 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 3 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 4 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 5 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400, lcddev.width, lcddev.height, 24, ( uint8_t * )DispChar, WHITE, BLACK );
}
else
{
LCD_ShowString( 106, 400 + vlist * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )DispChar, WHITE, BLACK );
}
vlist++;
} while( netbuf_next( recvbuf ) >= 0 );
netbuf_delete( recvbuf );
free( DispChar );
}
else
{
//__RS232_SVR_DESTROY:
netconn_close( newconn );
while( ERR_OK != netconn_delete( newconn ) )
{
vTaskDelay( 1 );
}
vlist = 0;
LCD_ShowString( 10, 370, lcddev.width, lcddev.height, 24, ( uint8_t * )"Tcp Client is Closed... ", WHITE, BLACK );
LCD_ShowString( 10, 400, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 0 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 1 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 2 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 3 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 4 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
LCD_ShowString( 106, 400 + 5 * 24, lcddev.width, lcddev.height, 24, ( uint8_t * )" ", WHITE, BLACK );
break;
}
}
}
vTaskDelay( 10 );
}
}
实验现象:
[localvideo]15c3959ecf0655aab5fcff303bfe996d[/localvideo]
工程源码:
下篇实现串口服务器功能,主要原理时,使用TCP服务器接收网络数据,然后通过串口发送出去;串口接收数据,通过网络发送出去。
-
发表了主题帖:
HC32F4A0_FreeRTOS_LwIP移植
HC32F4A0-FreeRTOS移植 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
上篇已经实现了FreeRTOS的移植,经过2天的努力硬肝,实现了LwIP协议栈带FreeRTOS实时系统的移植。板载网络芯片为RTL8021F,可以与单片机以MII或RMII的方式连接。这里,我选择的是RMII的方式,在实际项目中我是用的DP83848,也是使用RMII的方式,RMII方式使用的引脚更少,我们可以留出更多的管脚来控制其他设备。工程代码在文末哦。以下是RMII接口连接图:
以下是开发板网络接口部分原理图,板子兼容了RMII与MII接口,可以通过拨码开关选择接口方式,在编程时选择对应的方式即可。
移植LwIP协议栈有以下几个重要环节,网卡IO初始化、HC32F4A0内置以太网控制器初始化、网络中断回调函数编写、LwIP底层输入输出函数编写、sys_arch.c接口文件编写。LwIP文件较多,添加到编译器中截图不全,工程在最后上传,可以下载查看。以下是添加的LwIP文件和相关头文件路径。
下面将根据以上几个步骤实现LwIP协议栈移植。
网卡IO初始化(Ethernet_GpioInit),其中除了RMII接口用到的IO口外,还有网卡芯片的复位IO口,使用的是TCA9539端口2的PIN3。RMII接口用到的IO口为ETH_SMI_MDIO-->PA2、ETH_SMI_MDC--->PC1、ETH_RMII_TX_EN-->PG11、ETH_RMII_TXD0-->PG13、ETH_RMII_TXD1-->PG14、ETH_RMII_REF_CLK-->PA1、ETH_RMII_CRS_DV-->PA7、ETH_RMII_RXD0-->PC4、ETH_RMII_RXD1-->PC5、ETH_RMII_RX_ER-->PI10。在这里,我们只需要设置复用功能就可以了,仅针对HC32F4A0这款芯片,别的芯片可能还需要设置IO口的上下拉、速度等。以下是网口IO初始化代码:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] Ethernet_GpioInit
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-03-27
**************************************************************/
static void Ethernet_GpioInit( void )
{
/* ---解保护相关外设--- */
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO );
/* ---网卡芯片复位--- */
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN3, TCA9539_DIR_OUT );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN3, TCA9539_PIN_RESET );
delay_ms( PHY_HW_RST_DELAY );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN3, TCA9539_PIN_SET );
delay_ms( PHY_HW_RST_DELAY );
/* Configure MII/RMII selection IO for ETH */
#ifdef ETH_INTERFACE_RMII
/* Ethernet RMII pins configuration */
/*
ETH_SMI_MDIO ----------------> PA2
ETH_SMI_MDC -----------------> PC1
ETH_RMII_TX_EN --------------> PG11
ETH_RMII_TXD0 ---------------> PG13
ETH_RMII_TXD1 ---------------> PG14
ETH_RMII_REF_CLK ------------> PA1
ETH_RMII_CRS_DV -------------> PA7
ETH_RMII_RXD0 ---------------> PC4
ETH_RMII_RXD1 ---------------> PC5
ETH_RMII_RX_ER --------------> PI10
*/
/* Configure PA1, PA2 and PA7 */
GPIO_SetFunc( GPIO_PORT_A, ( GPIO_PIN_01 | GPIO_PIN_02 | GPIO_PIN_07 ), GPIO_FUNC_11 );
/* Configure PC1, PC4 and PC5 */
GPIO_SetFunc( GPIO_PORT_C, ( GPIO_PIN_01 | GPIO_PIN_04 | GPIO_PIN_05 ), GPIO_FUNC_11 );
/* Configure PG11, PG13 and PG14 */
GPIO_SetFunc( GPIO_PORT_G, ( GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14 ), GPIO_FUNC_11 );
/* Configure PI10 */
GPIO_SetFunc( GPIO_PORT_I, GPIO_PIN_10, GPIO_FUNC_11 );
#else
/* Ethernet MII pins configuration */
/*
ETH_SMI_MDIO ----------------> PA2
ETH_SMI_MDC -----------------> PC1
ETH_MII_TX_CLK --------------> PB6
ETH_MII_TX_EN ---------------> PG11
ETH_MII_TXD0 ----------------> PG13
ETH_MII_TXD1 ----------------> PG14
ETH_MII_TXD2 ----------------> PB9
ETH_MII_TXD3 ----------------> PB8
ETH_MII_RX_CLK --------------> PA1
ETH_MII_RX_DV ---------------> PA7
ETH_MII_RXD0 ----------------> PC4
ETH_MII_RXD1 ----------------> PC5
ETH_MII_RXD2 ----------------> PB0
ETH_MII_RXD3 ----------------> PB1
ETH_MII_RX_ER ---------------> PI10
ETH_MII_CRS -----------------> PH2
ETH_MII_COL -----------------> PH3
*/
/* Configure PA1, PA2 and PA7 */
GPIO_SetFunc( GPIO_PORT_A, ( GPIO_PIN_01 | GPIO_PIN_02 | GPIO_PIN_07 ), GPIO_FUNC_11 );
/* Configure PB0, PB1, PB6, PB8 and PB9 */
GPIO_SetFunc( GPIO_PORT_B, ( GPIO_PIN_00 | GPIO_PIN_01 | GPIO_PIN_06 | GPIO_PIN_08 | GPIO_PIN_09 ), GPIO_FUNC_11 );
/* Configure PC1, PC4 and PC5 */
GPIO_SetFunc( GPIO_PORT_C, ( GPIO_PIN_01 | GPIO_PIN_04 | GPIO_PIN_05 ), GPIO_FUNC_11 );
/* Configure PG11, PG13 and PG14 */
GPIO_SetFunc( GPIO_PORT_G, ( GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14 ), GPIO_FUNC_11 );
/* Configure PH2, PH3 */
GPIO_SetFunc( GPIO_PORT_H, ( GPIO_PIN_02 | GPIO_PIN_03 ), GPIO_FUNC_11 );
/* Configure PI10 */
GPIO_SetFunc( GPIO_PORT_I, GPIO_PIN_10, GPIO_FUNC_11 );
#endif
/* ---解保护相关外设--- */
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO );
}
HC32F4A0内置以太网控制器初始化(low_level_init),在这里主要涉及HC32F4A0的内置以太网控制器的设置、DMA的设置和网卡芯片的相关寄存器设置。在这个环节,除以上内容外,还创建了信号量,为系统和以太网数据接收提供桥梁,这个信号量可以是FreeRTOS的二值信号量,也可以是计数信号量。接着创建了网卡数据接收任务,将接收到的数据传给LwIP协议栈。以下是以太网控制器初始化的代码:
/**************************************************************
* @Name low_level_init
* @brief In this function, the hardware should be initialized.
* @param netif: [输入/出]
* @retval LL_OK: Initialize success
LL_ERR: Initialize failed
* @author Zachary
* @Data 2023-03-27
**************************************************************/
static int32_t low_level_init( struct netif *netif )
{
int32_t i32Ret = LL_ERR;
stc_eth_init_t stcEthInit;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO ); /* ---解保护相关外设--- */
/* ---打开网卡时钟--- */
FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_ETHMAC, ENABLE );
/* ---初始化网卡GPIO--- */
Ethernet_GpioInit();
/* ---网卡缺省设置--- */
ETH_DeInit();
/* ---相关寄存器缺省设置--- */
ETH_CommStructInit( &EthHandle.stcCommInit );
ETH_StructInit( &stcEthInit );
EthHandle.stcCommInit.u16PhyAddr = ETH_PHY_ADDR;
EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_RMII;
EthHandle.stcCommInit.u32ChecksumMode = ETH_MAC_CHECKSUM_MD_SW;
EthHandle.stcCommInit.u16AutoNego = ETH_AUTO_NEGO_ENABLE;
EthHandle.stcCommInit.u32Speed = ETH_MAC_SPEED_100M;
EthHandle.stcCommInit.u32DuplexMode = ETH_MAC_DUPLEX_MD_FULL;
EthHandle.stcCommInit.u32ReceiveMode = ETH_RX_MD_INT;
if( LL_OK == ETH_Init( &EthHandle, &stcEthInit ) )
{
netif->flags |= NETIF_FLAG_LINK_UP;
}
/* Initialize Tx Descriptors list: Chain Mode */
ETH_DMA_TxDescListInit( &EthHandle, EthDmaTxDscrTab, &EthTxBuff[0][0], ETH_TX_BUF_NUM );
/* Initialize Rx Descriptors list: Chain Mode */
ETH_DMA_RxDescListInit( &EthHandle, EthDmaRxDscrTab, &EthRxBuff[0][0], ETH_RX_BUF_NUM );
#if LWIP_ARP
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#else
netif->flags |= NETIF_FLAG_BROADCAST;
#endif /* LWIP_ARP */
/* 设置MAC地址长度 */
netif->hwaddr_len = 6U;
/* 设置MAC地址 地址已经初始化为默认2.0.0.0.0.0 */
netif->hwaddr[0] = Net_Info.MacAddr[0];
netif->hwaddr[1] = Net_Info.MacAddr[1];
netif->hwaddr[2] = Net_Info.MacAddr[2];
netif->hwaddr[3] = Net_Info.MacAddr[3];
netif->hwaddr[4] = Net_Info.MacAddr[4];
netif->hwaddr[5] = Net_Info.MacAddr[5];
/* maximum transfer unit */
netif->mtu = 1500U;
taskENTER_CRITICAL();
/* ---创建二值信号量或计数信号量--- */
if ( g_rx_semaphore == NULL )
{
//vSemaphoreCreateBinary( g_rx_semaphore );
//xSemaphoreTake( g_rx_semaphore, 0 );
g_rx_semaphore = xSemaphoreCreateCounting( 40, 0 );
}
/* 创建网卡任务 */
xTaskCreate( ethernetif_input, "ETHERNETIF_INPUT", ETHERNETIF_INPUT_TASK_STACK_SIZE, netif,
ETHERNETIF_INPUT_TASK_PRIO, &xTaskHandleEthernetInput );
taskEXIT_CRITICAL();
/* ---使能网卡MAC和DMA传输--- */
ETH_Start();
/* ---网卡接收中断设置--- */
Set_EthInterrupt();
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO ); /* ---解保护相关外设--- */
return i32Ret;
}
网络中断回调函数编写(Set_EthInterrupt),使用中断方式接收数据,是效率最高的方式。因此,网络数据的接收,我们不适用周期性查询的方式,我们使用以太网中断来实现。代码的实现很简单,跟串口中断的实现方式相同,在这了需要注意的是,根据FreeRTOS可管理的最高优先级来设置以太网中断的优先级,不可高于FreeRTOS可管理的最高优先级。以下是以太网中断设置函数代码和中断回调函数:
/**************************************************************
* @Name ETH_RxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-27
**************************************************************/
static void ETH_RxCpltCallback( void )
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
if( SET == ETH_DMA_GetStatus( ETH_DMA_FLAG_RIS ) )
{
xSemaphoreGiveFromISR( g_rx_semaphore, &xHigherPriorityTaskWoken );
}
ETH_DMA_ClearStatus( ETH_DMA_FLAG_RIS );
ETH_DMA_ClearStatus( ETH_DMA_FLAG_NIS );
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/**************************************************************
* @Name Set_EthInterrupt
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-27
**************************************************************/
static void Set_EthInterrupt( void )
{
stc_irq_signin_config_t stcIrqSigninConfig;
stcIrqSigninConfig.enIRQn = INT005_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_ETH_GLB_INT; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = Ð_RxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 5UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
}
LwIP底层输入输出函数编写(low_level_input、low_level_output),首先是与ST的官方函数基本一致,不同点在于变量的命名、函数的命名不同,这两个函数的实现,不用我们自己写,基本上带以太网控制器的都有配置代码,也可根据ST的改,基本上都是一模一样的。这两个函数是主要作用是底层数据的搬运。以下是完整的代码:
/**************************************************************
* @Name low_level_output
* @brief
* @param netif: [输入/出]
** p: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-27
**************************************************************/
static err_t low_level_output( struct netif *netif, struct pbuf *p )
{
int32_t i32Ret;
struct pbuf *q;
uint8_t *txBuffer;
__IO stc_eth_dma_desc_t *DmaTxDesc;
uint32_t byteCnt;
uint32_t frameLength = 0UL;
uint32_t bufferOffset;
uint32_t payloadOffset;
DmaTxDesc = EthHandle.stcTxDesc;
txBuffer = ( uint8_t * )( ( EthHandle.stcTxDesc )->u32Buf1Addr );
bufferOffset = 0UL;
/* Copy frame from pbufs to driver buffers */
for ( q = p; q != NULL; q = q->next )
{
/* If this buffer isn't available, goto error */
if ( 0UL != ( DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN ) )
{
i32Ret = LL_ERR;
goto error;
}
/* Get bytes in current buffer */
byteCnt = q->len;
payloadOffset = 0UL;
/* Check if the length of data to copy is bigger than Tx buffer size */
while ( ( byteCnt + bufferOffset ) > ETH_TX_BUF_SIZE )
{
/* Copy data to Tx buffer*/
memcpy( ( uint8_t * ) & ( txBuffer[bufferOffset] ), ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), ( ETH_TX_BUF_SIZE - bufferOffset ) );
/* Point to next descriptor */
DmaTxDesc = ( stc_eth_dma_desc_t * )( DmaTxDesc->u32Buf2NextDescAddr );
/* Check if the buffer is available */
if ( 0UL != ( DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN ) )
{
i32Ret = LL_ERR;
goto error;
}
txBuffer = ( uint8_t * )( DmaTxDesc->u32Buf1Addr );
byteCnt = byteCnt - ( ETH_TX_BUF_SIZE - bufferOffset );
payloadOffset = payloadOffset + ( ETH_TX_BUF_SIZE - bufferOffset );
frameLength = frameLength + ( ETH_TX_BUF_SIZE - bufferOffset );
bufferOffset = 0UL;
}
/* Copy the remaining bytes */
memcpy( ( uint8_t * ) & ( txBuffer[bufferOffset] ), ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), byteCnt );
bufferOffset = bufferOffset + byteCnt;
frameLength = frameLength + byteCnt;
}
/* Prepare transmit descriptors to give to DMA */
ETH_DMA_SetTransFrame( &EthHandle, frameLength );
i32Ret = LL_OK;
error:
/* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
if ( RESET != ETH_DMA_GetStatus( ETH_DMA_FLAG_UNS ) )
{
/* Clear DMA UNS flag */
ETH_DMA_ClearStatus( ETH_DMA_FLAG_UNS );
/* Resume DMA transmission */
WRITE_REG32( CM_ETH->DMA_TXPOLLR, 0UL );
}
return i32Ret;
}
/**************************************************************
* @Name low_level_input
* @brief
* @param netif: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-27
**************************************************************/
static struct pbuf * low_level_input( struct netif *netif )
{
struct pbuf *p = NULL;
struct pbuf *q;
uint32_t len;
uint8_t *rxBuffer;
__IO stc_eth_dma_desc_t *DmaRxDesc;
uint32_t byteCnt;
uint32_t bufferOffset;
uint32_t payloadOffset;
uint32_t i;
/* Get received frame */
if ( LL_OK != ETH_DMA_GetReceiveFrame( &EthHandle ) )
{
return NULL;
}
/* Obtain the size of the packet */
len = ( EthHandle.stcRxFrame ).u32Len;
rxBuffer = ( uint8_t * )( EthHandle.stcRxFrame ).u32Buf;
if ( len > 0UL )
{
/* Allocate a pbuf chain of pbufs from the buffer */
//p = ( struct pbuf * )malloc( sizeof( struct pbuf ) + len );
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
if ( NULL != p ) {
p->next = NULL;
p->payload = &( ( uint8_t * )p )[sizeof( struct pbuf )];
p->len = len;
( void )memset( p->payload, 0, p->len );
}
}
if ( p != NULL )
{
DmaRxDesc = ( EthHandle.stcRxFrame ).pstcFSDesc;
bufferOffset = 0UL;
for ( q = p; q != NULL; q = q->next )
{
byteCnt = q->len;
payloadOffset = 0UL;
/* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */
while ( ( byteCnt + bufferOffset ) > ETH_RX_BUF_SIZE )
{
/* Copy data to pbuf */
memcpy( ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), ( uint8_t * ) & ( rxBuffer[bufferOffset] ), ( ETH_RX_BUF_SIZE - bufferOffset ) );
/* Point to next descriptor */
DmaRxDesc = ( stc_eth_dma_desc_t * )( DmaRxDesc->u32Buf2NextDescAddr );
rxBuffer = ( uint8_t * )( DmaRxDesc->u32Buf1Addr );
byteCnt = byteCnt - ( ETH_RX_BUF_SIZE - bufferOffset );
payloadOffset = payloadOffset + ( ETH_RX_BUF_SIZE - bufferOffset );
bufferOffset = 0UL;
}
/* Copy remaining data in pbuf */
memcpy( ( uint8_t * ) & ( ( ( uint8_t * )q->payload )[payloadOffset] ), ( uint8_t * ) & ( rxBuffer[bufferOffset] ), byteCnt );
bufferOffset = bufferOffset + byteCnt;
}
}
/* Release descriptors to DMA */
DmaRxDesc = ( EthHandle.stcRxFrame ).pstcFSDesc;
for ( i = 0UL; i < ( EthHandle.stcRxFrame ).u32SegCount; i++ )
{
DmaRxDesc->u32ControlStatus |= ETH_DMA_RXDESC_OWN;
DmaRxDesc = ( stc_eth_dma_desc_t * )( DmaRxDesc->u32Buf2NextDescAddr );
}
/* Clear Segment_Count */
( EthHandle.stcRxFrame ).u32SegCount = 0UL;
/* When Rx Buffer unavailable flag is set, clear it and resume reception */
if ( RESET != ETH_DMA_GetStatus( ETH_DMA_FLAG_RUS ) )
{
/* Clear DMA RUS flag */
ETH_DMA_ClearStatus( ETH_DMA_FLAG_RUS );
/* Resume DMA reception */
WRITE_REG32( CM_ETH->DMA_RXPOLLR, 0UL );
}
return p;
}
以上几个步骤所对应的函数,我都放在ethernetif.c中,方便移植,可以理解为专门的网卡驱动.c文件。
sys_arch.c,这个文件是LwIP协议栈与FreeRTOS实时系统的沟通文件,主要有LwIP时基、线程保护、信号量互斥量的创建、释放、任务的创建等。这个文件是LwIP给出的接口文件,我们要根据不同的操作系统,来改写这个文件中的对应函数。代码如下:
#include "lwip/debug.h"
#include <lwip/opt.h>
#include <lwip/arch.h>
#include "lwip/tcpip.h"
#include "lwip/init.h"
#include "lwip/netif.h"
#include "lwip/sio.h"
#include "ethernetif.h"
#if !NO_SYS
#include "sys_arch.h"
#endif
#include <lwip/stats.h>
#include <lwip/debug.h>
#include <lwip/sys.h>
#include "lwip/dhcp.h"
#include <string.h>
int errno;
u32_t lwip_sys_now;
struct sys_timeouts
{
struct sys_timeo *next;
};
struct timeoutlist
{
struct sys_timeouts timeouts;
xTaskHandle pid;
};
#define SYS_THREAD_MAX 4
static struct timeoutlist s_timeoutlist[SYS_THREAD_MAX];
static u16_t s_nextthread = 0;
u32_t
sys_jiffies(void)
{
lwip_sys_now = xTaskGetTickCount();
return lwip_sys_now;
}
u32_t
sys_now(void)
{
lwip_sys_now = xTaskGetTickCount();
return lwip_sys_now;
}
void
sys_init(void)
{
int i;
// Initialize the the per-thread sys_timeouts structures
// make sure there are no valid pids in the list
for(i = 0; i < SYS_THREAD_MAX; i++)
{
s_timeoutlist[i].pid = 0;
s_timeoutlist[i].timeouts.next = NULL;
}
// keep track of how many threads have been created
s_nextthread = 0;
}
struct sys_timeouts *sys_arch_timeouts(void)
{
int i;
xTaskHandle pid;
struct timeoutlist *tl;
pid = xTaskGetCurrentTaskHandle( );
for(i = 0; i < s_nextthread; i++)
{
tl = &(s_timeoutlist[i]);
if(tl->pid == pid)
{
return &(tl->timeouts);
}
}
return NULL;
}
sys_prot_t sys_arch_protect(void)
{
vPortEnterCritical();
return 1;
}
void sys_arch_unprotect(sys_prot_t pval)
{
( void ) pval;
vPortExitCritical();
}
#if !NO_SYS
//test_sys_arch_waiting_fn the_waiting_fn;
//void
//test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn)
//{
// the_waiting_fn = waiting_fn;
//}
err_t
sys_sem_new(sys_sem_t *sem, u8_t count)
{
/* 创建 sem */
if(count <= 1)
{
*sem = xSemaphoreCreateBinary();
if(count == 1)
{
sys_sem_signal(sem);
}
}
else
*sem = xSemaphoreCreateCounting(count,count);
#if SYS_STATS
++lwip_stats.sys.sem.used;
if (lwip_stats.sys.sem.max < lwip_stats.sys.sem.used)
{
lwip_stats.sys.sem.max = lwip_stats.sys.sem.used;
}
#endif /* SYS_STATS */
if(*sem != SYS_SEM_NULL)
return ERR_OK;
else
{
#if SYS_STATS
++lwip_stats.sys.sem.err;
#endif /* SYS_STATS */
printf("[sys_arch]:new sem fail!\n");
return ERR_MEM;
}
}
void
sys_sem_free(sys_sem_t *sem)
{
#if SYS_STATS
--lwip_stats.sys.sem.used;
#endif /* SYS_STATS */
/* 删除 sem */
vSemaphoreDelete(*sem);
*sem = SYS_SEM_NULL;
}
int sys_sem_valid(sys_sem_t *sem)
{
return (*sem != SYS_SEM_NULL);
}
void
sys_sem_set_invalid(sys_sem_t *sem)
{
*sem = SYS_SEM_NULL;
}
/*
如果timeout参数不为零,则返回值为
等待信号量所花费的毫秒数。如果
信号量未在指定时间内发出信号,返回值为
SYS_ARCH_TIMEOUT。如果线程不必等待信号量
该函数返回零。 */
u32_t
sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
{
u32_t wait_tick = 0;
u32_t start_tick = 0 ;
//看看信号量是否有效
if(*sem == SYS_SEM_NULL)
return SYS_ARCH_TIMEOUT;
//首先获取开始等待信号量的时钟节拍
start_tick = xTaskGetTickCount();
//timeout != 0,需要将ms换成系统的时钟节拍
if(timeout != 0)
{
//将ms转换成时钟节拍
wait_tick = timeout / portTICK_PERIOD_MS;
if (wait_tick == 0)
wait_tick = 1;
}
else
wait_tick = portMAX_DELAY; //一直阻塞
//等待成功,计算等待的时间,否则就表示等待超时
if(xSemaphoreTake(*sem, wait_tick) == pdTRUE)
return ((xTaskGetTickCount()-start_tick)*portTICK_RATE_MS);
else
return SYS_ARCH_TIMEOUT;
}
void
sys_sem_signal(sys_sem_t *sem)
{
if(xSemaphoreGive( *sem ) != pdTRUE)
printf("[sys_arch]:sem signal fail!\n");
}
err_t
sys_mutex_new(sys_mutex_t *mutex)
{
/* 创建 sem */
*mutex = xSemaphoreCreateMutex();
if(*mutex != SYS_MRTEX_NULL)
return ERR_OK;
else
{
printf("[sys_arch]:new mutex fail!\n");
return ERR_MEM;
}
}
void
sys_mutex_free(sys_mutex_t *mutex)
{
vSemaphoreDelete(*mutex);
}
void
sys_mutex_set_invalid(sys_mutex_t *mutex)
{
*mutex = SYS_MRTEX_NULL;
}
void
sys_mutex_lock(sys_mutex_t *mutex)
{
xSemaphoreTake(*mutex,/* 互斥量句柄 */
portMAX_DELAY); /* 等待时间 */
}
void
sys_mutex_unlock(sys_mutex_t *mutex)
{
xSemaphoreGive( *mutex );//给出互斥量
}
sys_thread_t
sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
{
sys_thread_t handle = NULL;
BaseType_t xReturn = pdPASS;
/* 创建MidPriority_Task任务 */
xReturn = xTaskCreate((TaskFunction_t )function, /* 任务入口函数 */
(const char* )name,/* 任务名字 */
(uint16_t )stacksize, /* 任务栈大小 */
(void* )arg,/* 任务入口函数参数 */
(UBaseType_t )prio, /* 任务的优先级 */
(TaskHandle_t* )&handle);/* 任务控制块指针 */
if(xReturn != pdPASS)
{
printf("[sys_arch]:create task fail!err:%#lx\n",xReturn);
return NULL;
}
return handle;
}
err_t
sys_mbox_new(sys_mbox_t *mbox, int size)
{
/* 创建Test_Queue */
*mbox = xQueueCreate((UBaseType_t ) size,/* 消息队列的长度 */
(UBaseType_t ) sizeof(void *));/* 消息的大小 */
#if SYS_STATS
++lwip_stats.sys.mbox.used;
if (lwip_stats.sys.mbox.max < lwip_stats.sys.mbox.used)
{
lwip_stats.sys.mbox.max = lwip_stats.sys.mbox.used;
}
#endif /* SYS_STATS */
if(NULL == *mbox)
return ERR_MEM;
return ERR_OK;
}
void
sys_mbox_free(sys_mbox_t *mbox)
{
if( uxQueueMessagesWaiting( *mbox ) )
{
/* Line for breakpoint. Should never break here! */
portNOP();
#if SYS_STATS
lwip_stats.sys.mbox.err++;
#endif /* SYS_STATS */
// TODO notify the user of failure.
}
vQueueDelete(*mbox);
#if SYS_STATS
--lwip_stats.sys.mbox.used;
#endif /* SYS_STATS */
}
int sys_mbox_valid(sys_mbox_t *mbox)
{
if (*mbox == SYS_MBOX_NULL)
return 0;
else
return 1;
}
void
sys_mbox_set_invalid(sys_mbox_t *mbox)
{
*mbox = SYS_MBOX_NULL;
}
void
sys_mbox_post(sys_mbox_t *q, void *msg)
{
while(xQueueSend( *q, /* 消息队列的句柄 */
&msg,/* 发送的消息内容 */
portMAX_DELAY) != pdTRUE); /* 等待时间 */
}
err_t
sys_mbox_trypost(sys_mbox_t *q, void *msg)
{
if(xQueueSend(*q,&msg,0) == pdPASS)
return ERR_OK;
else
return ERR_MEM;
}
err_t
sys_mbox_trypost_fromisr(sys_mbox_t *q, void *msg)
{
return sys_mbox_trypost(q, msg);
}
u32_t
sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
{
void *dummyptr;
u32_t wait_tick = 0;
u32_t start_tick = 0 ;
if ( msg == NULL ) //看看存储消息的地方是否有效
msg = &dummyptr;
//首先获取开始等待信号量的时钟节拍
start_tick = sys_now();
//timeout != 0,需要将ms换成系统的时钟节拍
if(timeout != 0)
{
//将ms转换成时钟节拍
wait_tick = timeout / portTICK_PERIOD_MS;
if (wait_tick == 0)
wait_tick = 1;
}
//一直阻塞
else
wait_tick = portMAX_DELAY;
//等待成功,计算等待的时间,否则就表示等待超时
if(xQueueReceive(*q,&(*msg), wait_tick) == pdTRUE)
return ((sys_now() - start_tick)*portTICK_PERIOD_MS);
else
{
*msg = NULL;
return SYS_ARCH_TIMEOUT;
}
}
u32_t
sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
{
void *dummyptr;
if ( msg == NULL )
msg = &dummyptr;
//等待成功,计算等待的时间
if(xQueueReceive(*q,&(*msg), 0) == pdTRUE)
return ERR_OK;
else
return SYS_MBOX_EMPTY;
}
#if LWIP_NETCONN_SEM_PER_THREAD
#error LWIP_NETCONN_SEM_PER_THREAD==1 not supported
#endif /* LWIP_NETCONN_SEM_PER_THREAD */
#endif /* !NO_SYS */
以上都是底层移植步骤,接着是LwIP的初始化与网卡的挂载,都是调用LwIP的相关API就可以了。为方便验证,我这里使用的是静态IP,没有做DHCP。以下是代码:
/**
************************************* Copyright ******************************
*
* (C) Copyright 2022,Zachary ,HPU, China.
* All Rights Reserved
*
* FileName : netconf.c
* Version : v1.0
* Author : Zachary
* Date : 2022-02-17
* Description:
******************************************************************************
*/
/* Includes --------------------------------------------------------------*/
#include <stdio.h>
#include "bsp_NT35510.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/dhcp.h"
#include "ethernetif.h"
#include "app/netconf.h"
#include "lwip/tcpip.h"
#include "lwip/errno.h"
#include "os_includes.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
struct netif g_mynetif;
struct Network_Info Net_Info;
__IO uint8_t NetCable_STATUS;
/* --- Functions ---------------------------------------------------------*/
static void ethernetif_notify_conn_changed( struct netif *s_netif );
/*------------------------------------------------------------------------*/
/**************************************************************
* @Name LWIP_Init
* @brief 初始化网卡&LwIP协议栈
* @param None
* @retval
* @author Zachary
* @Data 2022-05-02
**************************************************************/
LwIP_StatusTypeDef LWIP_Init( void )
{
char DispBuf[50];
struct netif *Netif_Init_Flag;
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t gw;
tcpip_init( NULL, NULL );
LwIP_Set_Default_IP_Info( &Net_Info );
#if LWIP_DHCP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else
IP4_ADDR( &ipaddr, Net_Info.LocalIP[0], Net_Info.LocalIP[1], Net_Info.LocalIP[2], Net_Info.LocalIP[3] );
IP4_ADDR( &netmask, Net_Info.NetMask[0], Net_Info.NetMask[1], Net_Info.NetMask[2], Net_Info.NetMask[3] );
IP4_ADDR( &gw, Net_Info.GateWay[0], Net_Info.GateWay[1], Net_Info.GateWay[2], Net_Info.GateWay[3] );
LCD_ShowString( 10, 110, lcddev.width, lcddev.height, 24, ( uint8_t * )"IP:", WHITE, BLACK );
LCD_ShowString( 10, 140, lcddev.width, lcddev.height, 24, ( uint8_t * )"NetMask:", WHITE, BLACK );
LCD_ShowString( 10, 170, lcddev.width, lcddev.height, 24, ( uint8_t * )"GateWay:", WHITE, BLACK );
LCD_ShowString( 10, 200, lcddev.width, lcddev.height, 24, ( uint8_t * )"MacAddr:", WHITE, BLACK );
sprintf( DispBuf, "%03d.%03d.%03d.%03d", Net_Info.LocalIP[0], Net_Info.LocalIP[1], Net_Info.LocalIP[2], Net_Info.LocalIP[3] );
LCD_ShowString( 120, 110, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
sprintf( DispBuf, "%03d.%03d.%03d.%03d", Net_Info.NetMask[0], Net_Info.NetMask[1], Net_Info.NetMask[2], Net_Info.NetMask[3] );
LCD_ShowString( 120, 140, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
sprintf( DispBuf, "%03d.%03d.%03d.%03d", Net_Info.GateWay[0], Net_Info.GateWay[1], Net_Info.GateWay[2], Net_Info.GateWay[3] );
LCD_ShowString( 120, 170, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
sprintf( DispBuf, "%03d-%03d-%03d-%03d", Net_Info.MacAddr[0], Net_Info.MacAddr[1], Net_Info.MacAddr[2], Net_Info.MacAddr[3] );
LCD_ShowString( 120, 200, lcddev.width, lcddev.height, 24, ( uint8_t * )DispBuf, WHITE, BLACK );
LCD_ShowString( 10, 240, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP Stack Initializing...", WHITE, BLACK );
#if NET_DEBUG
printf( "网卡的MAC地址为...................%x.%x.%x.%x.%x.%x\r\n", Net_Info.MacAddr[0], Net_Info.MacAddr[1], Net_Info.MacAddr[2], Net_Info.MacAddr[3], Net_Info.MacAddr[4], Net_Info.MacAddr[5] );
printf( "静态IP地址........................%d.%d.%d.%d\r\n", Net_Info.LocalIP[0], Net_Info.LocalIP[1], Net_Info.LocalIP[2], Net_Info.LocalIP[3] );
printf( "子网掩码..........................%d.%d.%d.%d\r\n", Net_Info.NetMask[0], Net_Info.NetMask[1], Net_Info.NetMask[2], Net_Info.NetMask[3] );
printf( "默认网关..........................%d.%d.%d.%d\r\n", Net_Info.GateWay[0], Net_Info.GateWay[1], Net_Info.GateWay[2], Net_Info.GateWay[3] );
#endif /* #if NET_DEBUG */
#endif
Netif_Init_Flag = netif_add( &g_mynetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input );
/* registers the default network interface */
netif_set_default( &g_mynetif );
if( Netif_Init_Flag != NULL )
{
/* when the netif is fully configured this function must be called */
netif_set_up( &g_mynetif );
}
else
{
netif_set_down( &g_mynetif );
}
netif_set_link_callback( &g_mynetif, ethernetif_notify_conn_changed );
#if LWIP_DHCP
/* ---添加DHCP任务--- */
#endif
return NETIF_INIT_OK;
}
/**************************************************************
* @Name ethernetif_notify_conn_changed
* @brief
* @param Info: [输入/出]
* @retval
* @author Zachary
* @Data 2022-08-03
**************************************************************/
static void ethernetif_notify_conn_changed( struct netif *s_netif )
{
if( netif_is_link_up( s_netif ) )
{
NetCable_STATUS = 1;
netif_set_up( s_netif );
}
else
{
NetCable_STATUS = 0;
netif_set_down( s_netif );
}
}
/**************************************************************
* @Name LwIP_Set_Default_IP_Info
* @brief
* @param Info: [输入/出]
* @retval
* @author Zachary
* @Data 2022-05-02
**************************************************************/
void LwIP_Set_Default_IP_Info( struct Network_Info *Info )
{
Info->LocalIP[0] = 192;
Info->LocalIP[1] = 168;
Info->LocalIP[2] = 123;
Info->LocalIP[3] = 200;
Info->NetMask[0] = 255;
Info->NetMask[1] = 255;
Info->NetMask[2] = 255;
Info->NetMask[3] = 0;
Info->GateWay[0] = 192;
Info->GateWay[1] = 168;
Info->GateWay[2] = 123;
Info->GateWay[3] = 1;
Info->RemoteIP[0] = 192;
Info->RemoteIP[1] = 168;
Info->RemoteIP[2] = 123;
Info->RemoteIP[3] = 100;
Info->MacAddr[0] = 0x02;
Info->MacAddr[1] = 0x00;
Info->MacAddr[2] = 0x00;
Info->MacAddr[3] = 0x00;
Info->MacAddr[4] = 0x00;
Info->MacAddr[5] = 0x00;
}
接着我们在上篇创建的Task1中初始化以太网与LwIP协议栈,其代码如下:
/**************************************************************
* @Name xTask1
* @brief
* @param pvParameters: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask1( void *pvParameters )
{
LCD_ShowString( 10, 10, lcddev.width, lcddev.height, 24, ( uint8_t * )"HC32F4A0SITB", WHITE, BLACK );
LCD_ShowString( 10, 40, lcddev.width, lcddev.height, 24, ( uint8_t * )"FreeRTOS v10.3.0", WHITE, BLACK );
LCD_ShowString( 10, 70, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP v2.1.4", WHITE, BLACK );
if( NETIF_INIT_OK == LWIP_Init() )
{
LCD_ShowString( 10, 270, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP Stack Initialized Successfully...", WHITE, BLACK );
}
else
{
LCD_ShowString( 10, 270, lcddev.width, lcddev.height, 24, ( uint8_t * )"LwIP Stack Initialized Failed...", WHITE, BLACK );
}
for( ;; )
{
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN5, TCA9539_PIN_SET );
vTaskDelay( 1000 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN5, TCA9539_PIN_RESET );
vTaskDelay( 1000 );
}
}
以下是视频验证:
[localvideo]9364e4387afe8794744506db8660c26e[/localvideo]
以下是工程代码:
以下是原理图:
下篇,将用LwIP的NETCONN的API接口,实现TCP回显服务器和串口服务器。
硬肝出来的,LwIP一句两句真的说不清楚,其实还有lwipots.h文件是一个很重要的文件,协议栈所有的剪裁都在这个文件中实现,网上也有很多资料,我就不说明了。工程代码中也都有注释的。
- 2023-03-24
-
发表了主题帖:
HC32F4A0-FreeRTOS移植
本篇在上篇的基础上,进行实时操作系统移植,所使用到的系统是FreeRTOS v10.3.0,系统源码与测试代码都将以附件形式在文末分享。
由于我一直使用v10.3.0版本的系统,因此就不在git上下载最新的系统了,这个版本的系统我已经在多个产品上使用过了,稳定性与可靠性都不错。本篇只说系统如何使用。
首先添加源码文件到工程,如下图。除FreeRTOSConfig.h文件外,其余文件都是官方文件,不要自己更改其中的内容。FreeRTOSConfig.h这个文件是非常重要的,是用于剪裁FreeRTOS这个系统的,比如是否使用信号量、是否使用软件定时器等。
其次,还有一个重要文件为port.c,这个文件根据内核或编译平台的不同,要选择不同的文件,我们这里使用ARM_CM4F文件夹下的port.c,如图所示。
在添加完相关文件后,我们需要添加头文件路径,如下图。
由于HC32F4A0的svc中断和pendsv中断本身没有函数写好,都需要我们自己去实现,而FreeRTOS是需要接管这两个函数的,所以我们就不需要创建也不需要删除相关内容。但是,我们需要实现Systick_Handler函数,因为之前我们使用过精确定时,所以,我们将这个函数放在delay.c中,像ST或GD单片机,一般都放在stm(gd)32f4xx_it.c中。Systick_Handler函数实现如下。在delay.c中,还需要包含相关FreeRTOS的头文件,我们新建了一个os_inclusdes.h的头文件,包含了所有操作系统相关的头文件,其内容有#include "FreeRTOS.h"、#include "task.h"、#include "queue.h"、#include "timers.h"、#include "semphr.h"。
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] SysTick_Handler
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-02-13
**************************************************************/
extern void xPortSysTickHandler( void );
void SysTick_Handler( void )
{
uint32_t ulReturn;
if ( xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED ) /* ---任务调度已开启--- */
{
ulReturn = taskENTER_CRITICAL_FROM_ISR();
xPortSysTickHandler();
taskEXIT_CRITICAL_FROM_ISR( ulReturn );
}
}
接着我们编写多任务函数,我们一共创建3个任务来测试操作系统是否一直成功,分别是static void xTaskStart( void *pvParameters )开始任务、static void xTask1( void *pvParameters )任务1和static void xTask2( void *pvParameters )任务2。
首先,需要创建任务句柄,任务句柄每个任务都是唯一的,任务的创建、挂起、删除等操作,都需要任务句柄。有了任务句柄,就可以使用xTaskCreate函数创建任务了。下面是我们在main.c中创建的任务。任务创建函数的说明均以注释的形式,进行说明。开始任务中创建了任务1与任务2,开始任务本身优先级为最低0,任务内容为控制LED1每500ms闪烁一次;任务1优先级为1,任务内容是控制LCD液晶屏刷屏;任务2优先级最高,任务内容是控制LED2每250ms闪烁一次。
static TaskHandle_t xTaskHandleStart = NULL; /* ---任务句柄--- */
static TaskHandle_t xTaskHandleTask1 = NULL;
static TaskHandle_t xTaskHandleTask2 = NULL;
static void xTaskStart( void *pvParameters ); /* ---任务函数--- */
static void xTask1( void *pvParameters );
static void xTask2( void *pvParameters );
/**************************************************************
* @Name StartTaskCreate
* @brief 创建开始任务
* @param None
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
void StartTaskCreate( void )
{
xTaskCreate( xTaskStart, /* ---任务函数--- */
"xTaskStart", /* ---任务名称--- */
1024, /* ---任务堆栈--- */
NULL, /* ---传入参数--- */
0, /* ---任务优先级--- */
&xTaskHandleStart ); /* ---任务句柄--- */
}
/**************************************************************
* @Name xTaskStart
* @brief
* @param pvParameters: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTaskStart( void *pvParameters )
{
xTaskCreate( xTask1, /* ---任务函数--- */
"xTask1", /* ---任务名称--- */
512, /* ---任务堆栈--- */
NULL, /* ---传入参数--- */
1, /* ---任务优先级--- */
&xTaskHandleTask1 ); /* ---任务句柄--- */
xTaskCreate( xTask2, /* ---任务函数--- */
"xTask2", /* ---任务名称--- */
512, /* ---任务堆栈--- */
NULL, /* ---传入参数--- */
2, /* ---任务优先级--- */
&xTaskHandleTask2 ); /* ---任务句柄--- */
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 xTask1
* @brief
* @param pvParameters: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask1( void *pvParameters )
{
for( ;; )
{
LCD_Clear( RED );
vTaskDelay( 200 );
LCD_Clear( GREEN );
vTaskDelay( 200 );
LCD_Clear( BLACK );
vTaskDelay( 200 );
LCD_Clear( BLUE );
vTaskDelay( 200 );
LCD_Clear( YELLOW );
vTaskDelay( 200 );
LCD_Clear( MAGENTA );
vTaskDelay( 200 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
vTaskDelay( 1000 );
}
}
/**************************************************************
* @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 );
}
}
下面是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 "os_includes.h"
#include "bsp_Gpio.h"
#include "bsp_TCA9539.h"
#include "bsp_NT35510.h"
#include "bsp_XPT2046.h"
#include "bsp_AT24Cxx.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
stc_clock_freq_t sysclk;
uint8_t Reg = 0x20;
uint8_t ReadBuf[8];
uint8_t WriteBuf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
char DispBuf1[50], DispBuf2[50];
static TaskHandle_t xTaskHandleStart = NULL;
static TaskHandle_t xTaskHandleTask1 = NULL;
static TaskHandle_t xTaskHandleTask2 = NULL;
/* --- Functions ---------------------------------------------------------*/
static void SysInit( void );
static void bsp_SetSysClk( void );
static void LCD_Test( void );
static void xTaskStart( void *pvParameters );
static void xTask1( void *pvParameters );
static void xTask2( void *pvParameters );
/*------------------------------------------------------------------------*/
/**************************************************************
* @Name Load_Drow_Dialog
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
void Load_Drow_Dialog( void )
{
LCD_Clear( WHITE );//清屏
LCD_ShowString( lcddev.width - 24, 0, lcddev.width, lcddev.height, 16, ( uint8_t * )"RST", BLACK, WHITE ); //显示清屏区域
}
/**************************************************************
* @Name rtp_test
* @brief 电阻触摸屏测试函数
* @param None
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
void rtp_test( void )
{
Load_Drow_Dialog();
while( 1 )
{
tp_dev.scan( 0 );
if( tp_dev.sta & TP_PRES_DOWN ) //触摸屏被按下
{
if( tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height )
{
if( tp_dev.x[0] > ( lcddev.width - 24 ) && tp_dev.y[0] < 16 )
Load_Drow_Dialog();//清除
else
TP_Draw_Big_Point( tp_dev.x[0], tp_dev.y[0], RED ); //画图
}
}
else
delay_ms( 1 ); //没有按键按下的时候
}
}
/**************************************************************
* @Name xTask1
* @brief
* @param pvParameters: [输入/出]
* @retval
* @author Zachary
* @Data 2023-03-23
**************************************************************/
static void xTask1( void *pvParameters )
{
for( ;; )
{
LCD_Clear( RED );
vTaskDelay( 200 );
LCD_Clear( GREEN );
vTaskDelay( 200 );
LCD_Clear( BLACK );
vTaskDelay( 200 );
LCD_Clear( BLUE );
vTaskDelay( 200 );
LCD_Clear( YELLOW );
vTaskDelay( 200 );
LCD_Clear( MAGENTA );
vTaskDelay( 200 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
vTaskDelay( 1000 );
}
}
/**************************************************************
* @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 )
{
xTaskCreate( xTask1, /* ---任务函数--- */
"xTask1", /* ---任务名称--- */
512, /* ---任务堆栈--- */
NULL, /* ---传入参数--- */
1, /* ---任务优先级--- */
&xTaskHandleTask1 ); /* ---任务句柄--- */
xTaskCreate( xTask2, /* ---任务函数--- */
"xTask2", /* ---任务名称--- */
512, /* ---任务堆栈--- */
NULL, /* ---传入参数--- */
2, /* ---任务优先级--- */
&xTaskHandleTask2 ); /* ---任务句柄--- */
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();
// sprintf( DispBuf1, "WirteBuf:%d,%d,%d,%d,%d,%d,%d,%d", WriteBuf[0], WriteBuf[1], \
// WriteBuf[2], WriteBuf[3], WriteBuf[4], WriteBuf[5], WriteBuf[6], WriteBuf[7] );
// LCD_ShowString( 10, 500, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf1, WHITE, BLACK );
// AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 0x01, WriteBuf, 0x08 );
// delay_ms( 10 );
// AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 0x01, ReadBuf, 0x08 );
// sprintf( DispBuf2, "ReadBuf:%d,%d,%d,%d,%d,%d,%d,%d", ReadBuf[0], ReadBuf[1], \
// ReadBuf[2], ReadBuf[3], ReadBuf[4], ReadBuf[5], ReadBuf[6], ReadBuf[7] );
// LCD_ShowString( 10, 550, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf2, WHITE, BLACK );
// delay_ms( 1000 );
// rtp_test();
StartTaskCreate();
vTaskStartScheduler();
while( 1 )
{
// LED10_TOGGLE();
// LED11_TOGGLE();
// TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
// delay_ms( 500 );
// TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
// delay_ms( 500 );
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
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_NT35510_Init();
// LCD_Test();
bsp_XPT2046_Init();
}
/**************************************************************
* @Name LCD_Test
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-04
**************************************************************/
static void LCD_Test( void )
{
LCD_Clear( RED );
delay_ms( 500 );
LCD_Clear( GREEN );
delay_ms( 500 );
LCD_Clear( BLACK );
delay_ms( 500 );
LCD_Clear( BLUE );
delay_ms( 500 );
LCD_Clear( YELLOW );
delay_ms( 500 );
LCD_Clear( MAGENTA );
delay_ms( 500 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
}
/**************************************************************
* @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 ); /* ---保护相关外设--- */
}
视频展示:
[localvideo]61196ecc4f1fc4dd65dcaeda6f71b91c[/localvideo]
FreeRTOS源码与测试工程代码:
- 2023-03-22
-
回复了主题帖:
HC32F4A0-硬件I2C应用-AT24C02
lugl4313820 发表于 2023-3-22 11:15
可以直接取出入到OLED上吗?我感觉也有点小麻烦,而且还吃内存。
可以啊,又不是很难。实现了显示一个字符,新建一个函数,传入参数为指针,将数组首地址传进去,在函数中将数组中的数据+0x30转为ascii码,挨个显示就行了。
sprintf只是将数据拼接起来,我觉得用起来很方便。比如显示浮点是,也可以直接用,现在单片机这么强大,跟何况我现在用的是240MHz的ARM单片机,又不是8051。一般我用到液晶,基本上就是用来做人机界面的。
-
发表了主题帖:
HC32F4A0-硬件SPI应用-触摸屏
前期已经实现了LCD液晶屏的显示,这次实现电阻触摸屏的使用。电阻触摸屏的原理再次不在赘述,可以去看原子教程的原理讲解,其中校准函数等都参考了原子的代码。校准完成之后需要将校准系数等保存到AT24C02中的前13个字节,上篇我们已经是现实了硬件I2C来将数据写入AT24C02并读取,我们可以直接拿来使用。
首先,先介绍一下HC32F4A0的硬件SPI。HC32F4A0共有6个硬件SPI,支持高速全双工通讯,数据格式有多种选择,并且可以设置3线或4线SPI,波特率可以根据实际调整。本篇使用到的是SPI6,4线全双工模式,数据格式为MSB先行,波特率根据实际情况调整为PLCK1/64,PCLK为120MHz。下面是波特率设置图。
本篇使用到的SPI6对应的管脚是SCK->PE4、MOSI->PE5、MISO->PE5、CS->TCA9539的P06、Pen_IRQ->TCA9539的P04。根据数据手册43页的表2-1引脚功能表可知,通讯复用功能组为FG2,再根据51页的表2-2 Fun32-63表(如下图)可知,SPI6_SCK、SPI6_MOSI、SPI6_MISO的复用号分别为Func46、Func47、Func48。
用到的电阻触摸屏是用XPT2046这个芯片,这是一个12位4通道的ADC芯片。下面是XPT2046电路图、通讯时序图。
通过发送相应的命令字,即可从xpt2046中读回数据,从时序图中可知,读回来的是一个12位的数据。在程序中的实现方式是读回两个8位数据,再将数据合并。以下是实现代码:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] TP_Read_AD
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param CMD: [输入/出]
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2022-11-09
**************************************************************/
uint16_t TP_Read_AD( uint8_t CMD )
{
uint16_t Num = 0;
T_CS_LOW(); //选中触摸屏IC
xpt2046_spi_send_byte( CMD ); //发送命令字
Num = 0x0;
Num = xpt2046_spi_send_byte( 0x00 ) << 8;
Num |= xpt2046_spi_send_byte( 0x00 );
Num >>= 4;
T_CS_HIGH(); //释放片选
Num &= 0x0FFF;
return( Num );
}
上述代码中使用到的xpt2046_spi_send_byte()函数是驱动触摸屏的关键函数,用的是查询法读SPI,先等待发送寄存器为空,发送字节数据,查询接收寄存器,再返回数据,其代码如下:
uint16_t xpt2046_spi_send_byte( uint8_t byte )
{
/* 等待发送寄存器为空 */
while( RESET == SPI_GetStatus( T_SPIx, SPI_FLAG_TX_BUF_EMPTY ) );
/* 发送一个字节数据 */
SPI_WriteData( T_SPIx, byte );
/* 等待接收寄存器不为空 */
while( RESET == SPI_GetStatus( T_SPIx, SPI_FLAG_RX_BUF_FULL ) );
/* 返回收到的数据 */
return SPI_ReadData( T_SPIx );
}
了解了相关电路与关键代码,开始配置SPI,以下是SPI的配置,包括管脚配置和SPI配置,其中管脚配置需要注意,除了配置复用功能外,其他如管脚的上下拉、输入输出等都不要配置,否则SPI会读不到数据,卡在while( RESET == SPI_GetStatus( T_SPIx, SPI_FLAG_RX_BUF_FULL ) ),保持默认就好。其余参数配置都做了相关注释。
/**************************************************************
* @Name bsp_XPT2046_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void bsp_XPT2046_Init( void )
{
bsp_XPT2046_GPIO_Init();
bsp_XPT2046_SPI_Init();
delay_ms( 100 );
TP_Read_XY( &tp_dev.x[0], &tp_dev.y[0] ); //第一次读取初始化
if( TP_Get_Adjdata() )
{
return; //已经校准
}
else //未校准?
{
//#ifdef SSD1963
// SSD_LCD_Clear( WHITE ); //清屏
//#else
// LCD_Clear( WHITE ); //清屏
//#endif
TP_Adjust(); //屏幕校准
}
TP_Get_Adjdata();
}
/**************************************************************
* @Name bsp_XPT2046_GPIO_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
static void bsp_XPT2046_GPIO_Init( void )
{
// stc_gpio_init_t GPIO_InitStruct = { 0 };
LL_PERIPH_WE( LL_PERIPH_GPIO );
/* Config T_SCK_PIN */
// GPIO_StructInit( &GPIO_InitStruct );
// GPIO_InitStruct.u16PinDrv = PIN_HIGH_DRV;
// GPIO_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
// GPIO_InitStruct.u16PullUp = PIN_PU_ON;
// GPIO_InitStruct.u16PinOutputType = PIN_OUT_TYPE_NMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
// GPIO_Init( T_SCK_PORT, T_SCK_PIN, &GPIO_InitStruct );
/* Config T_MISO_PIN */
// GPIO_StructInit( &GPIO_InitStruct );
// GPIO_InitStruct.u16PinDir = PIN_DIR_IN; /* --方向 输入或输出--- */
// GPIO_InitStruct.u16PullUp = PIN_PU_ON;
// GPIO_Init( T_MISO_PORT, T_MISO_PIN, &GPIO_InitStruct );
/* Config T_MOSI_PIN */
// GPIO_StructInit( &GPIO_InitStruct );
// GPIO_InitStruct.u16PinDrv = PIN_HIGH_DRV;
// GPIO_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
// GPIO_InitStruct.u16PullUp = PIN_PU_ON;
// GPIO_InitStruct.u16PinOutputType = PIN_OUT_TYPE_NMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
// GPIO_Init( T_MOSI_PORT, T_MOSI_PIN, &GPIO_InitStruct );
/* Config T_CS_PIN */
TCA9539_ConfigPin( T_CS_PORT, T_CS_PIN, TCA9539_DIR_OUT );
/* Config T_PEN_IRQ_PIN */
TCA9539_ConfigPin( T_PEN_IRQ_PORT, T_PEN_IRQ_PIN, TCA9539_DIR_IN );
/* ---复用设置--- */
GPIO_SetFunc( T_SCK_PORT, T_SCK_PIN, GPIO_FUNC_46 );
GPIO_SetFunc( T_MOSI_PORT, T_MOSI_PIN, GPIO_FUNC_47 );
GPIO_SetFunc( T_MISO_PORT, T_MISO_PIN, GPIO_FUNC_48 );
LL_PERIPH_WP( LL_PERIPH_GPIO );
}
/**************************************************************
* @Name bsp_XPT2046_SPI_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
static void bsp_XPT2046_SPI_Init( void )
{
stc_spi_init_t spi_init_struct;
stc_spi_delay_t stcSpiDelayCfg;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO );
T_CS_HIGH(); /* ---拉高片选--- */
SPI_StructInit( &spi_init_struct );
SPI_DelayStructInit( &stcSpiDelayCfg );
FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_SPI6, ENABLE ); /* ---失能SPI6时钟--- */
SPI_DeInit( T_SPIx ); /* ---反初始化SPI6--- */
spi_init_struct.u32BaudRatePrescaler = SPI_BR_CLK_DIV64; /* ---波特率 PCLK1 / 64--- */
spi_init_struct.u32DataBits = SPI_DATA_SIZE_8BIT; /* ---8位数据长度--- */
spi_init_struct.u32FirstBit = SPI_FIRST_MSB; /* ---高位先行--- */
spi_init_struct.u32FrameLevel = SPI_1_FRAME; /* ---帧数--- */
spi_init_struct.u32MasterSlave = SPI_MASTER; /* ---主机模式--- */
spi_init_struct.u32ModeFaultDetect = SPI_MD_FAULT_DETECT_DISABLE;
spi_init_struct.u32Parity = SPI_PARITY_INVD; /* ---禁用校验--- */
spi_init_struct.u32SpiMode = SPI_MD_0; /* ---模式0-- */
spi_init_struct.u32SuspendMode = SPI_COM_SUSP_FUNC_OFF;
spi_init_struct.u32TransMode = SPI_FULL_DUPLEX; /* ---全双工--- */
spi_init_struct.u32WireMode = SPI_4_WIRE; /* ---四线制--- */
SPI_Init( T_SPIx, &spi_init_struct );
stcSpiDelayCfg.u32IntervalDelay = SPI_INTERVAL_TIME_8SCK; /* ---数据延时--- */
stcSpiDelayCfg.u32ReleaseDelay = SPI_RELEASE_TIME_8SCK;
stcSpiDelayCfg.u32SetupDelay = SPI_SETUP_TIME_1SCK;
SPI_DelayTimeConfig( T_SPIx, &stcSpiDelayCfg );
SPI_Cmd( T_SPIx, ENABLE ); /* ---失能SPI6--- */
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO );
}
以下是读电阻触摸屏AD值、校准等相关代码,参考了原子的。
/**************************************************************
* @Name TP_Read_AD
* @brief
* @param CMD: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint16_t TP_Read_AD( uint8_t CMD )
{
uint16_t Num = 0;
T_CS_LOW(); //选中触摸屏IC
xpt2046_spi_send_byte( CMD ); //发送命令字
Num = 0x0;
Num = xpt2046_spi_send_byte( 0x00 ) << 8;
Num |= xpt2046_spi_send_byte( 0x00 );
Num >>= 4;
T_CS_HIGH(); //释放片选
Num &= 0x0FFF;
return( Num );
}
/**************************************************************
* @Name TP_Read_XOY
* @brief 读取一个坐标值(x或者y)
* @param xy: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
#define READ_TIMES 5 //读取次数
#define LOST_VAL 2 //丢弃值
uint16_t TP_Read_XOY( uint8_t xy )
{
uint16_t i, j;
uint16_t buf[READ_TIMES];
uint16_t sum = 0;
uint16_t temp;
for( i = 0; i < READ_TIMES; i++ )
buf[i] = TP_Read_AD( xy );
for( i = 0; i < READ_TIMES - 1; i++ ) //排序
{
for( j = i + 1; j < READ_TIMES; j++ )
{
if( buf[i] > buf[j] ) //升序排列
{
temp = buf[i];
buf[i] = buf[j];
buf[j] = temp;
}
}
}
sum = 0;
for( i = LOST_VAL; i < READ_TIMES - LOST_VAL; i++ )sum += buf[i];
temp = sum / ( READ_TIMES - 2 * LOST_VAL );
return temp;
}
/**************************************************************
* @Name TP_Read_XY
* @brief 读取x,y坐标
* @param x: [输入/出]
** y: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint8_t TP_Read_XY( uint16_t *x, uint16_t *y )
{
uint16_t xtemp, ytemp;
xtemp = TP_Read_XOY( CMD_RDX );
ytemp = TP_Read_XOY( CMD_RDY );
if( xtemp < 100 || ytemp < 100 )
return 0;//读数失败
*x = xtemp;
*y = ytemp;
return 1;//读数成功
}
/**************************************************************
* @Name TP_Read_XY2
* @brief
* @param x: [输入/出]
** y: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
#define ERR_RANGE 50 //误差范围
uint8_t TP_Read_XY2( uint16_t *x, uint16_t *y )
{
uint16_t x1, y1;
uint16_t x2, y2;
uint8_t flag;
flag = TP_Read_XY( &x1, &y1 );
if( flag == 0 )
return( 0 );
flag = TP_Read_XY( &x2, &y2 );
if( flag == 0 )
return( 0 );
if( ( ( x2 <= x1 && x1 < x2 + ERR_RANGE ) || ( x1 <= x2 && x2 < x1 + ERR_RANGE ) ) //前后两次采样在+-50内
&& ( ( y2 <= y1 && y1 < y2 + ERR_RANGE ) || ( y1 <= y2 && y2 < y1 + ERR_RANGE ) ) )
{
*x = ( x1 + x2 ) / 2;
*y = ( y1 + y2 ) / 2;
return 1;
} else return 0;
}
//与LCD部分有关的函数
//画一个触摸点
//用来校准用的
//x,y:坐标
//color:颜色
void TP_Drow_Touch_Point( uint16_t x, uint16_t y, uint16_t color )
{
#ifdef SSD1963
LCD_DrawLine( x - 12, y, x + 13, y, color ); //横线
LCD_DrawLine( x, y - 12, x, y + 13, color ); //竖线
SSD_LCD_Fast_DrawPoint( x + 1, y + 1, color );
SSD_LCD_Fast_DrawPoint( x - 1, y + 1, color );
SSD_LCD_Fast_DrawPoint( x + 1, y - 1, color );
SSD_LCD_Fast_DrawPoint( x - 1, y - 1, color );
LCD_Draw_Circle( x, y, 6, color ); //画中心圈
#else
LCD_DrawLine( x - 12, y, x + 13, y, color ); //横线
LCD_DrawLine( x, y - 12, x, y + 13, color ); //竖线
LCD_DrawPoint( x + 1, y + 1, color );
LCD_DrawPoint( x - 1, y + 1, color );
LCD_DrawPoint( x + 1, y - 1, color );
LCD_DrawPoint( x - 1, y - 1, color );
LCD_Draw_Circle( x, y, 6, color ); //画中心圈
#endif
}
//画一个大点(2*2的点)
//x,y:坐标
//color:颜色
void TP_Draw_Big_Point( uint16_t x, uint16_t y, uint16_t color )
{
#ifdef SSD1963
SSD_LCD_Fast_DrawPoint( x, y, color ); //中心点
SSD_LCD_Fast_DrawPoint( x + 1, y, color );
SSD_LCD_Fast_DrawPoint( x, y + 1, color );
SSD_LCD_Fast_DrawPoint( x + 1, y + 1, color );
#else
LCD_DrawPoint( x, y, color ); //中心点
LCD_DrawPoint( x + 1, y, color );
LCD_DrawPoint( x, y + 1, color );
LCD_DrawPoint( x + 1, y + 1, color );
#endif
}
/**************************************************************
* @Name TP_Scan
* @brief
* @param tp: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint8_t TP_Scan( uint8_t tp )
{
uint8_t KeyStat = 0x01;
TCA9539_ReadPin( T_PEN_IRQ_PORT, T_PEN_IRQ_PIN, &KeyStat );
if( KeyStat == RESET ) //有按键按下
{
if( tp )
{
TP_Read_XY2( &tp_dev.x[0], &tp_dev.y[0] ); //读取物理坐标
}
else if( TP_Read_XY2( &tp_dev.x[0], &tp_dev.y[0] ) ) //读取屏幕坐标
{
tp_dev.x[0] = tp_dev.xfac * tp_dev.x[0] + tp_dev.xoff; //将结果转换为屏幕坐标
tp_dev.y[0] = tp_dev.yfac * tp_dev.y[0] + tp_dev.yoff;
}
if( ( tp_dev.sta & TP_PRES_DOWN ) == 0 ) //之前没有被按下
{
tp_dev.sta = TP_PRES_DOWN | TP_CATH_PRES; //按键按下
tp_dev.x[4] = tp_dev.x[0]; //记录第一次按下时的坐标
tp_dev.y[4] = tp_dev.y[0];
}
}
else
{
if( tp_dev.sta & TP_PRES_DOWN ) //之前是被按下的
{
tp_dev.sta &= ~( 1 << 7 ); //标记按键松开
}
else//之前就没有被按下
{
tp_dev.x[4] = 0;
tp_dev.y[4] = 0;
tp_dev.x[0] = 0xffff;
tp_dev.y[0] = 0xffff;
}
}
return tp_dev.sta & TP_PRES_DOWN; //返回当前的触屏状态
}
#define SAVE_ADDR_BASE 0
//保存校准参数
void TP_Save_Adjdata( void )
{
uint8_t Reg;
uint8_t tempbuf[8];
int32_t temp;
//保存校正结果!
temp = tp_dev.xfac * 100000000; //保存x校正因素
tempbuf[0] = ( temp & 0xFF000000 ) >> 24;
tempbuf[1] = ( temp & 0x00FF0000 ) >> 16;
tempbuf[2] = ( temp & 0x0000FF00 ) >> 8;
tempbuf[3] = ( temp & 0x000000FF );
Reg = SAVE_ADDR_BASE;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
delay_ms( 20 );
temp = tp_dev.yfac * 100000000; //保存y校正因素
tempbuf[0] = ( temp & 0xFF000000 ) >> 24;
tempbuf[1] = ( temp & 0x00FF0000 ) >> 16;
tempbuf[2] = ( temp & 0x0000FF00 ) >> 8;
tempbuf[3] = ( temp & 0x000000FF );
Reg = SAVE_ADDR_BASE + 4;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
delay_ms( 20 );
//保存x偏移量
tempbuf[0] = ( tp_dev.xoff & 0xFF00 ) >> 8;
tempbuf[1] = tp_dev.xoff & 0x00FF;
Reg = SAVE_ADDR_BASE + 8;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
delay_ms( 20 );
//保存y偏移量
tempbuf[0] = ( tp_dev.yoff & 0xFF00 ) >> 8;
tempbuf[1] = tp_dev.yoff & 0x00FF;
Reg = SAVE_ADDR_BASE + 10;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
delay_ms( 20 );
//保存触屏类型
tempbuf[0] = tp_dev.touchtype;
Reg = SAVE_ADDR_BASE + 12;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
delay_ms( 20 );
tempbuf[0] = 0X0A; //标记校准过了
Reg = SAVE_ADDR_BASE + 13;
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
}
//得到保存在EEPROM里面的校准值
//返回值:1,成功获取数据
// 0,获取失败,要重新校准
uint8_t TP_Get_Adjdata( void )
{
uint8_t Reg, tempbuf[4];
int32_t tempfac;
Reg = SAVE_ADDR_BASE + 13;
// tempbuf[0] = 0x00;
// AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );//读取标记字,看是否校准过!
if( tempbuf[0] == 0x0A ) //触摸屏已经校准过了
{
Reg = SAVE_ADDR_BASE;
//tempfac = FM24CL16_ReadLenByte( SAVE_ADDR_BASE, 4 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
tempfac = tempbuf[0] << 24 | tempbuf[1] << 16 | tempbuf[2] << 8 | tempbuf[3];
tp_dev.xfac = ( float )tempfac / 100000000; //得到x校准参数
Reg = SAVE_ADDR_BASE + 4;
//tempfac = FM24CL16_ReadLenByte( SAVE_ADDR_BASE + 4, 4 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 4U );
tempfac = tempbuf[0] << 24 | tempbuf[1] << 16 | tempbuf[2] << 8 | tempbuf[3];
tp_dev.yfac = ( float )tempfac / 100000000; //得到y校准参数
//得到x偏移量
//tp_dev.xoff = FM24CL16_ReadLenByte( SAVE_ADDR_BASE + 8, 2 );
Reg = SAVE_ADDR_BASE + 8;
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
tempfac = tempbuf[0] << 8 | tempbuf[1];
tp_dev.xoff = tempfac;
//得到y偏移量
//tp_dev.yoff = FM24CL16_ReadLenByte( SAVE_ADDR_BASE + 10, 2 );
Reg = SAVE_ADDR_BASE + 10;
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 2U );
tempfac = tempbuf[0] << 8 | tempbuf[1];
tp_dev.yoff = tempfac;
Reg = SAVE_ADDR_BASE + 12;
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 1U, &tempbuf[0], 1U );
tempfac = tempbuf[0];
tp_dev.touchtype = tempfac; //读取触屏类型标记
if( tp_dev.touchtype ) //X,Y方向与屏幕相反
{
CMD_RDX = 0X90;
CMD_RDY = 0XD0;
} else //X,Y方向与屏幕相同
{
CMD_RDX = 0XD0;
CMD_RDY = 0X90;
}
return 1;
}
return 0;
}
//提示校准结果(各个参数)
void TP_Adj_Info_Show( uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, uint16_t fac )
{
LCD_ShowString( 40, 160, lcddev.width, lcddev.height, 16, ( uint8_t * )"x1:", RED, GBLUE );
LCD_ShowString( 40 + 80, 160, lcddev.width, lcddev.height, 16, ( uint8_t * )"y1:", RED, GBLUE );
LCD_ShowString( 40, 180, lcddev.width, lcddev.height, 16, ( uint8_t * )"x2:", RED, GBLUE );
LCD_ShowString( 40 + 80, 180, lcddev.width, lcddev.height, 16, ( uint8_t * )"y2:", RED, GBLUE );
LCD_ShowString( 40, 200, lcddev.width, lcddev.height, 16, ( uint8_t * )"x3:", RED, GBLUE );
LCD_ShowString( 40 + 80, 200, lcddev.width, lcddev.height, 16, ( uint8_t * )"y3:", RED, GBLUE );
LCD_ShowString( 40, 220, lcddev.width, lcddev.height, 16, ( uint8_t * )"x4:", RED, GBLUE );
LCD_ShowString( 40 + 80, 220, lcddev.width, lcddev.height, 16, ( uint8_t * )"y4:", RED, GBLUE );
LCD_ShowString( 40, 240, lcddev.width, lcddev.height, 16, ( uint8_t * )"fac is:", RED, GBLUE );
LCD_ShowNum( 40 + 24, 160, x0, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 160, y0, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24, 180, x1, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 180, y1, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24, 200, x2, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 200, y2, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24, 220, x3, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 24 + 80, 220, y3, 4, 16, RED, GBLUE ); //显示数值
LCD_ShowNum( 40 + 56, 240, fac, 3, 16, RED, GBLUE ); //显示数值,该数值必须在95~105范围之内.
}
//触摸屏校准代码
//得到四个校准参数
uint8_t* const TP_REMIND_MSG_TBL = ( uint8_t * )"Please use the stylus click the cross on the screen.The cross will always move until the screen adjustment is completed.";
void TP_Adjust( void )
{
uint16_t pos_temp[4][2];//坐标缓存值
uint8_t cnt = 0;
uint16_t d1, d2;
uint32_t tem1, tem2;
double fac;
uint16_t outtime = 0;
cnt = 0;
#ifdef SSD1963
SSD_LCD_Clear( WHITE ); //清屏
#else
LCD_Clear( WHITE );
#endif
#ifdef SSD1963
SSD_LCD_ShowString( 40, 40, 160, 100, 16, ( uint8_t* )TP_REMIND_MSG_TBL, RED, GBLUE ); //显示提示信息
#else
LCD_ShowString( 40, 40, 160, 100, 16, ( uint8_t* )TP_REMIND_MSG_TBL, RED, GBLUE ); //显示提示信息
#endif
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
tp_dev.sta = 0; //消除触发信号
tp_dev.xfac = 0; //xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
while( 1 ) //如果连续10秒钟没有按下,则自动退出
{
tp_dev.scan( 1 ); //扫描物理坐标
if( ( tp_dev.sta & 0xc0 ) == TP_CATH_PRES ) //按键按下了一次(此时按键松开了.)
{
outtime = 0;
tp_dev.sta &= ~( 1 << 6 ); //标记按键已经被处理过了.
pos_temp[cnt][0] = tp_dev.x[0];
pos_temp[cnt][1] = tp_dev.y[0];
cnt++;
switch( cnt )
{
case 1:
TP_Drow_Touch_Point( 20, 20, WHITE ); //清除点1
TP_Drow_Touch_Point( lcddev.width - 20, 20, RED ); //画点2
break;
case 2:
TP_Drow_Touch_Point( lcddev.width - 20, 20, WHITE ); //清除点2
TP_Drow_Touch_Point( 20, lcddev.height - 20, RED ); //画点3
break;
case 3:
TP_Drow_Touch_Point( 20, lcddev.height - 20, WHITE ); //清除点3
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, RED ); //画点4
break;
case 4: //全部四个点已经得到
//对边相等
tem1 = abs( pos_temp[0][0] - pos_temp[1][0] ); //x1-x2
tem2 = abs( pos_temp[0][1] - pos_temp[1][1] ); //y1-y2
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt( tem1 + tem2 ); //得到1,2的距离
tem1 = abs( pos_temp[2][0] - pos_temp[3][0] ); //x3-x4
tem2 = abs( pos_temp[2][1] - pos_temp[3][1] ); //y3-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt( tem1 + tem2 ); //得到3,4的距离
fac = ( float )d1 / d2;
if( fac < 0.95 || fac > 1.05 || d1 == 0 || d2 == 0 ) //不合格
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
TP_Adj_Info_Show( pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0], pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100 ); //显示数据
continue;
}
tem1 = abs( pos_temp[0][0] - pos_temp[2][0] ); //x1-x3
tem2 = abs( pos_temp[0][1] - pos_temp[2][1] ); //y1-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt( tem1 + tem2 ); //得到1,3的距离
tem1 = abs( pos_temp[1][0] - pos_temp[3][0] ); //x2-x4
tem2 = abs( pos_temp[1][1] - pos_temp[3][1] ); //y2-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt( tem1 + tem2 ); //得到2,4的距离
fac = ( float )d1 / d2;
if( fac < 0.95 || fac > 1.05 ) //不合格
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
TP_Adj_Info_Show( pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0], pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100 ); //显示数据
continue;
}//正确了
//对角线相等
tem1 = abs( pos_temp[1][0] - pos_temp[2][0] ); //x1-x3
tem2 = abs( pos_temp[1][1] - pos_temp[2][1] ); //y1-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt( tem1 + tem2 ); //得到1,4的距离
tem1 = abs( pos_temp[0][0] - pos_temp[3][0] ); //x2-x4
tem2 = abs( pos_temp[0][1] - pos_temp[3][1] ); //y2-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt( tem1 + tem2 ); //得到2,3的距离
fac = ( float )d1 / d2;
if( fac < 0.95 || fac > 1.05 ) //不合格
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
TP_Adj_Info_Show( pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0], pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100 ); //显示数据
continue;
}//正确了
//计算结果
tp_dev.xfac = ( float )( lcddev.width - 40 ) / ( pos_temp[1][0] - pos_temp[0][0] ); //得到xfac
tp_dev.xoff = ( lcddev.width - tp_dev.xfac * ( pos_temp[1][0] + pos_temp[0][0] ) ) / 2; //得到xoff
tp_dev.yfac = ( float )( lcddev.height - 40 ) / ( pos_temp[2][1] - pos_temp[0][1] ); //得到yfac
tp_dev.yoff = ( lcddev.height - tp_dev.yfac * ( pos_temp[2][1] + pos_temp[0][1] ) ) / 2; //得到yoff
if( fabs( tp_dev.xfac ) > 2 || fabs( tp_dev.yfac ) > 2 ) //触屏和预设的相反了.
{
cnt = 0;
TP_Drow_Touch_Point( lcddev.width - 20, lcddev.height - 20, WHITE ); //清除点4
TP_Drow_Touch_Point( 20, 20, RED ); //画点1
LCD_ShowString( 40, 26, lcddev.width, lcddev.height, 16, ( uint8_t * )"TP Need readjust!", RED, GBLUE );
tp_dev.touchtype = !tp_dev.touchtype; //修改触屏类型.
if( tp_dev.touchtype ) //X,Y方向与屏幕相反
{
CMD_RDX = 0X90;
CMD_RDY = 0XD0;
} else //X,Y方向与屏幕相同
{
CMD_RDX = 0XD0;
CMD_RDY = 0X90;
}
continue;
}
LCD_Clear( WHITE ); //清屏
LCD_ShowString( 35, 110, lcddev.width, lcddev.height, 16, ( uint8_t * )"Touch Screen Adjust OK!", GREEN, GBLUE ); //校正完成
delay_ms( 1000 );
TP_Save_Adjdata();
LCD_Clear( WHITE ); //清屏
return;//校正完成
}
}
delay_ms( 10 );
outtime++;
if( outtime > 1000 )
{
TP_Get_Adjdata();
break;
}
}
}
以下是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"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
stc_clock_freq_t sysclk;
uint8_t Reg = 0x20;
uint8_t ReadBuf[8];
uint8_t WriteBuf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
char DispBuf1[50], DispBuf2[50];
/* --- Functions ---------------------------------------------------------*/
static void SysInit( void );
static void bsp_SetSysClk( void );
static void LCD_Test( void );
/*------------------------------------------------------------------------*/
void Load_Drow_Dialog( void )
{
LCD_Clear( WHITE );//清屏
LCD_ShowString( lcddev.width - 24, 0, lcddev.width, lcddev.height, 16, ( uint8_t * )"RST", BLACK, WHITE ); //显示清屏区域
}
//电阻触摸屏测试函数
void rtp_test( void )
{
Load_Drow_Dialog();
while( 1 )
{
tp_dev.scan( 0 );
if( tp_dev.sta & TP_PRES_DOWN ) //触摸屏被按下
{
if( tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height )
{
if( tp_dev.x[0] > ( lcddev.width - 24 ) && tp_dev.y[0] < 16 )
Load_Drow_Dialog();//清除
else
TP_Draw_Big_Point( tp_dev.x[0], tp_dev.y[0], RED ); //画图
}
}
else
delay_ms( 1 ); //没有按键按下的时候
}
}
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-22
**************************************************************/
int32_t main( void )
{
SysInit();
sprintf( DispBuf1, "WirteBuf:%d,%d,%d,%d,%d,%d,%d,%d", WriteBuf[0], WriteBuf[1], \
WriteBuf[2], WriteBuf[3], WriteBuf[4], WriteBuf[5], WriteBuf[6], WriteBuf[7] );
LCD_ShowString( 10, 500, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf1, WHITE, BLACK );
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 0x01, WriteBuf, 0x08 );
delay_ms( 10 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 0x01, ReadBuf, 0x08 );
sprintf( DispBuf2, "ReadBuf:%d,%d,%d,%d,%d,%d,%d,%d", ReadBuf[0], ReadBuf[1], \
ReadBuf[2], ReadBuf[3], ReadBuf[4], ReadBuf[5], ReadBuf[6], ReadBuf[7] );
LCD_ShowString( 10, 550, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf2, WHITE, BLACK );
delay_ms( 1000 );
rtp_test();
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
delay_ms( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
delay_ms( 500 );
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
bsp_NT35510_Init();
LCD_Test();
bsp_XPT2046_Init();
}
/**************************************************************
* @Name LCD_Test
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-04
**************************************************************/
static void LCD_Test( void )
{
LCD_Clear( RED );
delay_ms( 500 );
LCD_Clear( GREEN );
delay_ms( 500 );
LCD_Clear( BLACK );
delay_ms( 500 );
LCD_Clear( BLUE );
delay_ms( 500 );
LCD_Clear( YELLOW );
delay_ms( 500 );
LCD_Clear( MAGENTA );
delay_ms( 500 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
}
/**************************************************************
* @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 ); /* ---保护相关外设--- */
}
以下是视频验证:
[localvideo]c7e6dbe004ce7da9faca3dcba99999dc[/localvideo]
以下是代码附件:
- 2023-03-21
-
发表了主题帖:
HC32F4A0-硬件I2C应用-AT24C02
本篇测试硬件I2C读写AT24C02,正好用到的触摸屏是电阻屏,可以将系数存到EEPROM中。
板载AT24C02硬件电路如下图,跟TCA9539挂在一条I2C总线上,该总线上还挂了很多别的外设,在总线出设有上拉电阻。
由于我已经初始化过TCA9539,AT24C02与它共用一条I2C总线。因此,波特率不变的情况下,只需初始化一次就可以了。甚至可以调用TCA9539的读写代码,是一模一样的。
但是为了区分,和方便以后移植,分别写了相关函数。
以下是初始化代码:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] bsp_AT2Cxx_Init
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2022-02-09
**************************************************************/
void bsp_AT2Cxx_Init( void )
{
uint8_t i;
int32_t i32Ret = LL_ERR;
float32_t fErr;
stc_i2c_init_t stcI2cInit;
stc_gpio_init_t stcGpioInit;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
/* Configuration I2C GPIO */
GPIO_StructInit( &stcGpioInit );
GPIO_Init( AT24Cxx_I2C_SCL_PORT, AT24Cxx_I2C_SCL_PIN, &stcGpioInit );
GPIO_Init( AT24Cxx_I2C_SDA_PORT, AT24Cxx_I2C_SDA_PIN, &stcGpioInit );
GPIO_SetFunc( AT24Cxx_I2C_SCL_PORT, AT24Cxx_I2C_SCL_PIN, GPIO_FUNC_49 );
GPIO_SetFunc( AT24Cxx_I2C_SDA_PORT, AT24Cxx_I2C_SDA_PIN, GPIO_FUNC_48 );
/* Enable I2C Peripheral*/
AT24Cxx_I2C_FCG_ENABLE();
I2C_DeInit( AT24Cxx_I2Cx );
I2C_StructInit( &stcI2cInit );
stcI2cInit.u32Baudrate = 100000UL;
stcI2cInit.u32SclTime = 0U;
stcI2cInit.u32ClockDiv = I2C_CLK_DIV16;
for ( i = 0U; i < 5U; i++ )
{
i32Ret = I2C_Init( AT24Cxx_I2Cx, &stcI2cInit, &fErr );
if ( LL_OK != i32Ret )
{
stcI2cInit.u32ClockDiv--;
} else
{
break;
}
}
if ( LL_OK == i32Ret )
{
I2C_BusWaitCmd( AT24Cxx_I2Cx, ENABLE );
}
I2C_Cmd( AT24Cxx_I2Cx, ENABLE );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
}
以下是读写AT24C02代码,我是一比一对照IO口模拟时序来写的,并且仅注释了IO口模拟的代码,方便对照。
/**************************************************************
* @Name AT24Cxx_I2C_Read
* @brief
* @param ReadAddr: [输入/出]
* @retval
* @author Zachary
* @Data 2022-02-09
**************************************************************/
void AT24Cxx_I2C_Read( uint16_t u16DevAddr, const uint8_t *pu8Reg, uint8_t u8RegLen, uint8_t *pu8Buf, uint32_t u32Len )
{
int32_t i32Ret;
I2C_SWResetCmd( AT24Cxx_I2Cx, ENABLE );
I2C_SWResetCmd( AT24Cxx_I2Cx, DISABLE );
i32Ret = I2C_Start( AT24Cxx_I2Cx, I2C_TIMEOUT );
if( LL_OK == i32Ret )
{
i32Ret = I2C_TransAddr( AT24Cxx_I2Cx, u16DevAddr, I2C_DIR_TX, I2C_TIMEOUT );
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( AT24Cxx_I2Cx, pu8Reg, u8RegLen, I2C_TIMEOUT );
if ( LL_OK == i32Ret )
{
i32Ret = I2C_Restart( AT24Cxx_I2Cx, I2C_TIMEOUT );
if ( LL_OK == i32Ret )
{
if ( 1UL == u32Len )
{
I2C_AckConfig( AT24Cxx_I2Cx, I2C_NACK );
}
i32Ret = I2C_TransAddr( AT24Cxx_I2Cx, u16DevAddr, I2C_DIR_RX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_MasterReceiveDataAndStop( AT24Cxx_I2Cx, pu8Buf, u32Len, I2C_TIMEOUT ); /* ---开始数据接收--- */
}
I2C_AckConfig( AT24Cxx_I2Cx, I2C_ACK );
}
}
}
}
if ( LL_OK != i32Ret )
{
I2C_Stop( AT24Cxx_I2Cx, I2C_TIMEOUT );
}
// i2c_start();
// i2c_send_onebyte( add_h ); //发送器件地址0XA0,写数据
// i2c_wait_ack();
// i2c_send_onebyte( add_l ); //发送低地址
// i2c_wait_ack();
// i2c_start();
// i2c_send_onebyte( 0XA1 + ( ( ReadAddr / 256 ) << 1 ) ); //进入接收模式
// i2c_wait_ack();
// temp = i2c_read_onebyte( 0 );
// i2c_stop(); //产生一个停止条件
// return temp;
}
/**************************************************************
* @Name AT24Cxx_I2C_Write
* @brief
* @param WriteAddr: [输入/出]
** DataToWrite: [输入/出]
* @retval
* @author Zachary
* @Data 2022-02-09
**************************************************************/
void AT24Cxx_I2C_Write( uint16_t u16DevAddr, const uint8_t *pu8Reg, uint8_t u8RegLen, const uint8_t *pu8Buf, uint32_t u32Len )
{
int32_t i32Ret;
I2C_SWResetCmd( AT24Cxx_I2Cx, ENABLE );
I2C_SWResetCmd( AT24Cxx_I2Cx, DISABLE );
i32Ret = I2C_Start( AT24Cxx_I2Cx, I2C_TIMEOUT );
if( LL_OK == i32Ret )
{
i32Ret = I2C_TransAddr( AT24Cxx_I2Cx, u16DevAddr, I2C_DIR_TX, I2C_TIMEOUT );
if( LL_OK == i32Ret )
{
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( AT24Cxx_I2Cx, pu8Reg, u8RegLen, I2C_TIMEOUT );
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( AT24Cxx_I2Cx, pu8Buf, u32Len, I2C_TIMEOUT );
}
}
}
}
I2C_Stop( AT24Cxx_I2Cx, I2C_TIMEOUT );
// i2c_start();
// i2c_send_onebyte( add_h ); //写
// i2c_wait_ack();
// i2c_send_onebyte( add_l ); //发送低地址
// i2c_wait_ack();
// i2c_send_onebyte( DataToWrite ); //发送字节
// i2c_wait_ack();
// i2c_stop(); //产生一个停止条件
// delay_ms( 2 );
}
因为已经实现了液晶显示,所以直接使用液晶来显示读写情况,没有液晶则可以使用串口打印。
液晶显示读写代码,使用到了sprintf函数,打包要显示的数据。
sprintf( DispBuf1, "WirteBuf:%d,%d,%d,%d,%d,%d,%d,%d", WriteBuf[0], WriteBuf[1], \
WriteBuf[2], WriteBuf[3], WriteBuf[4], WriteBuf[5], WriteBuf[6], WriteBuf[7] );
LCD_ShowString( 10, 500, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf1, WHITE, BLACK );
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 0x01, WriteBuf, 0x08 );
delay_ms( 10 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 0x01, ReadBuf, 0x08 );
sprintf( DispBuf2, "ReadBuf:%d,%d,%d,%d,%d,%d,%d,%d", ReadBuf[0], ReadBuf[1], \
ReadBuf[2], ReadBuf[3], ReadBuf[4], ReadBuf[5], ReadBuf[6], ReadBuf[7] );
LCD_ShowString( 10, 550, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf2, WHITE, BLACK );
以下是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_AT24Cxx.h"
/* --- Typedef -----------------------------------------------------------*/
/* --- Define ------------------------------------------------------------*/
/* --- Macro -------------------------------------------------------------*/
/* --- Variables ---------------------------------------------------------*/
stc_clock_freq_t sysclk;
uint8_t Reg = 0x00;
uint8_t ReadBuf[8];
uint8_t WriteBuf[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
char DispBuf1[50], DispBuf2[50];
/* --- Functions ---------------------------------------------------------*/
static void SysInit( void );
static void bsp_SetSysClk( void );
static void LCD_Test( void );
/*------------------------------------------------------------------------*/
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-02-13
**************************************************************/
int32_t main( void )
{
SysInit();
sprintf( DispBuf1, "WirteBuf:%d,%d,%d,%d,%d,%d,%d,%d", WriteBuf[0], WriteBuf[1], \
WriteBuf[2], WriteBuf[3], WriteBuf[4], WriteBuf[5], WriteBuf[6], WriteBuf[7] );
LCD_ShowString( 10, 500, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf1, WHITE, BLACK );
AT24Cxx_I2C_Write( AT24Cxx_Addr, &Reg, 0x01, WriteBuf, 0x08 );
delay_ms( 10 );
AT24Cxx_I2C_Read( AT24Cxx_Addr, &Reg, 0x01, ReadBuf, 0x08 );
sprintf( DispBuf2, "ReadBuf:%d,%d,%d,%d,%d,%d,%d,%d", ReadBuf[0], ReadBuf[1], \
ReadBuf[2], ReadBuf[3], ReadBuf[4], ReadBuf[5], ReadBuf[6], ReadBuf[7] );
LCD_ShowString( 10, 550, lcddev.width, lcddev.height, 24, ( uint8_t *)DispBuf2, WHITE, BLACK );
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
delay_ms( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
delay_ms( 500 );
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
bsp_NT35510_Init();
LCD_Test();
}
/**************************************************************
* @Name LCD_Test
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-04
**************************************************************/
static void LCD_Test( void )
{
LCD_Clear( RED );
delay_ms( 500 );
LCD_Clear( GREEN );
delay_ms( 500 );
LCD_Clear( BLACK );
delay_ms( 500 );
LCD_Clear( BLUE );
delay_ms( 500 );
LCD_Clear( YELLOW );
delay_ms( 500 );
LCD_Clear( MAGENTA );
delay_ms( 500 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
}
/**************************************************************
* @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 ); /* ---保护相关外设--- */
}
测试视频:
[localvideo]cd1502bbad3f8c5a4e6f7c5dcdf08198[/localvideo]
测试工程代码:
- 2023-03-05
-
加入了学习《Zigbee系列教程》,观看 协议栈工作流程和无线收发控制LED
-
回复了主题帖:
HC32F4A0-I2C应用
秦天qintian0303 发表于 2023-3-5 00:48
硬件IIC的配置最大支持什么速度?
400Kbps
-
回复了主题帖:
GD407替代ST207问题
用GD32F470替换吧,470问题少点,不过IO口驱动能力和抗干扰没有ST好
-
发表了主题帖:
HC32F4A0-TFTLCD
本期测评HC32F4A0的EXMC外部存储器控制器来驱动TFTLCD液晶屏,板载的液晶屏接口完全兼容正点原子的液晶屏,正好手上有一块咸鱼上50块买的仿正点原子的4.0寸液晶屏,下图是液晶屏的原理图。
HC32F4A0的EXMC控制器又细分为NFC、DMC和SMC,我们使用SMC来控制液晶,相当于GDM32F103的FSMC控制器。与GD、ST不同的是,SMC的地中空间起始地址不是由片选来定的,且地址空间是可自定义的。比如GD的使用NE0,那么起始地址是0x60000000,使用NE1,那么起始地址就为0x64000000,如下图所示。而HC32F4A0无论片选是什么,起始地址都为0x60000000。那么如果要确定LCD的起始地址,就必须确定地址线,如开发板用到的是ADD12,那么LCD的地址就是0x60000000+((1 << 13)-2)=0x60001FFE。这个定义倒是与GD和ST相同,外设存储宽度为16位,内部都要右移一位。但是在手册中并没有找到相关的说明,可以参考GD或ST的说明,如下图。
ST的说明。
以下是LCD液晶屏SMC配置代码:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] bsp_NT35510_Init
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2022-11-09
**************************************************************/
void bsp_NT35510_Init( void )
{
bsp_NT35510_Gpio_Init();
bsp_NT35510_EXMC_Init();
delay_ms( 100 );
//发送秘钥
LCD_WriteReg( 0xF000, 0x0055 );
LCD_WriteReg( 0xF001, 0x00AA );
LCD_WriteReg( 0xF002, 0x0052 );
LCD_WriteReg( 0xF003, 0x0008 );
LCD_WriteReg( 0xF004, 0x0001 );
LCD_WR_REG( 0xC500 ); //读取ID高8位
lcddev.id = LCD_RD_DATA(); //读回0X55
lcddev.id <<= 8;
LCD_WR_REG( 0xC501 ); //读取ID低8位
lcddev.id |= LCD_RD_DATA(); //读回0X10
delay_ms( 5 );
if( lcddev.id == 0x8000 )
lcddev.id = 0x5510; //NT35510读回的ID是8000H,为方便区分,我们强制设置为5510
if( lcddev.id == 0x5510 )
{
LCD_WriteReg( 0xF000, 0x55 );
LCD_WriteReg( 0xF001, 0xAA );
LCD_WriteReg( 0xF002, 0x52 );
LCD_WriteReg( 0xF003, 0x08 );
LCD_WriteReg( 0xF004, 0x01 );
//AVDD Set AVDD 5.2V
LCD_WriteReg( 0xB000, 0x0D );
LCD_WriteReg( 0xB001, 0x0D );
LCD_WriteReg( 0xB002, 0x0D );
//AVDD ratio
LCD_WriteReg( 0xB600, 0x34 );
LCD_WriteReg( 0xB601, 0x34 );
LCD_WriteReg( 0xB602, 0x34 );
//AVEE -5.2V
LCD_WriteReg( 0xB100, 0x0D );
LCD_WriteReg( 0xB101, 0x0D );
LCD_WriteReg( 0xB102, 0x0D );
//AVEE ratio
LCD_WriteReg( 0xB700, 0x34 );
LCD_WriteReg( 0xB701, 0x34 );
LCD_WriteReg( 0xB702, 0x34 );
//VCL -2.5V
LCD_WriteReg( 0xB200, 0x00 );
LCD_WriteReg( 0xB201, 0x00 );
LCD_WriteReg( 0xB202, 0x00 );
//VCL ratio
LCD_WriteReg( 0xB800, 0x24 );
LCD_WriteReg( 0xB801, 0x24 );
LCD_WriteReg( 0xB802, 0x24 );
//VGH 15V (Free pump)
LCD_WriteReg( 0xBF00, 0x01 );
LCD_WriteReg( 0xB300, 0x0F );
LCD_WriteReg( 0xB301, 0x0F );
LCD_WriteReg( 0xB302, 0x0F );
//VGH ratio
LCD_WriteReg( 0xB900, 0x34 );
LCD_WriteReg( 0xB901, 0x34 );
LCD_WriteReg( 0xB902, 0x34 );
//VGL_REG -10V
LCD_WriteReg( 0xB500, 0x08 );
LCD_WriteReg( 0xB501, 0x08 );
LCD_WriteReg( 0xB502, 0x08 );
LCD_WriteReg( 0xC200, 0x03 );
//VGLX ratio
LCD_WriteReg( 0xBA00, 0x24 );
LCD_WriteReg( 0xBA01, 0x24 );
LCD_WriteReg( 0xBA02, 0x24 );
//VGMP/VGSP 4.5V/0V
LCD_WriteReg( 0xBC00, 0x00 );
LCD_WriteReg( 0xBC01, 0x78 );
LCD_WriteReg( 0xBC02, 0x00 );
//VGMN/VGSN -4.5V/0V
LCD_WriteReg( 0xBD00, 0x00 );
LCD_WriteReg( 0xBD01, 0x78 );
LCD_WriteReg( 0xBD02, 0x00 );
//VCOM
LCD_WriteReg( 0xBE00, 0x00 );
LCD_WriteReg( 0xBE01, 0x64 );
//Gamma Setting
LCD_WriteReg( 0xD100, 0x00 );
LCD_WriteReg( 0xD101, 0x33 );
LCD_WriteReg( 0xD102, 0x00 );
LCD_WriteReg( 0xD103, 0x34 );
LCD_WriteReg( 0xD104, 0x00 );
LCD_WriteReg( 0xD105, 0x3A );
LCD_WriteReg( 0xD106, 0x00 );
LCD_WriteReg( 0xD107, 0x4A );
LCD_WriteReg( 0xD108, 0x00 );
LCD_WriteReg( 0xD109, 0x5C );
LCD_WriteReg( 0xD10A, 0x00 );
LCD_WriteReg( 0xD10B, 0x81 );
LCD_WriteReg( 0xD10C, 0x00 );
LCD_WriteReg( 0xD10D, 0xA6 );
LCD_WriteReg( 0xD10E, 0x00 );
LCD_WriteReg( 0xD10F, 0xE5 );
LCD_WriteReg( 0xD110, 0x01 );
LCD_WriteReg( 0xD111, 0x13 );
LCD_WriteReg( 0xD112, 0x01 );
LCD_WriteReg( 0xD113, 0x54 );
LCD_WriteReg( 0xD114, 0x01 );
LCD_WriteReg( 0xD115, 0x82 );
LCD_WriteReg( 0xD116, 0x01 );
LCD_WriteReg( 0xD117, 0xCA );
LCD_WriteReg( 0xD118, 0x02 );
LCD_WriteReg( 0xD119, 0x00 );
LCD_WriteReg( 0xD11A, 0x02 );
LCD_WriteReg( 0xD11B, 0x01 );
LCD_WriteReg( 0xD11C, 0x02 );
LCD_WriteReg( 0xD11D, 0x34 );
LCD_WriteReg( 0xD11E, 0x02 );
LCD_WriteReg( 0xD11F, 0x67 );
LCD_WriteReg( 0xD120, 0x02 );
LCD_WriteReg( 0xD121, 0x84 );
LCD_WriteReg( 0xD122, 0x02 );
LCD_WriteReg( 0xD123, 0xA4 );
LCD_WriteReg( 0xD124, 0x02 );
LCD_WriteReg( 0xD125, 0xB7 );
LCD_WriteReg( 0xD126, 0x02 );
LCD_WriteReg( 0xD127, 0xCF );
LCD_WriteReg( 0xD128, 0x02 );
LCD_WriteReg( 0xD129, 0xDE );
LCD_WriteReg( 0xD12A, 0x02 );
LCD_WriteReg( 0xD12B, 0xF2 );
LCD_WriteReg( 0xD12C, 0x02 );
LCD_WriteReg( 0xD12D, 0xFE );
LCD_WriteReg( 0xD12E, 0x03 );
LCD_WriteReg( 0xD12F, 0x10 );
LCD_WriteReg( 0xD130, 0x03 );
LCD_WriteReg( 0xD131, 0x33 );
LCD_WriteReg( 0xD132, 0x03 );
LCD_WriteReg( 0xD133, 0x6D );
LCD_WriteReg( 0xD200, 0x00 );
LCD_WriteReg( 0xD201, 0x33 );
LCD_WriteReg( 0xD202, 0x00 );
LCD_WriteReg( 0xD203, 0x34 );
LCD_WriteReg( 0xD204, 0x00 );
LCD_WriteReg( 0xD205, 0x3A );
LCD_WriteReg( 0xD206, 0x00 );
LCD_WriteReg( 0xD207, 0x4A );
LCD_WriteReg( 0xD208, 0x00 );
LCD_WriteReg( 0xD209, 0x5C );
LCD_WriteReg( 0xD20A, 0x00 );
LCD_WriteReg( 0xD20B, 0x81 );
LCD_WriteReg( 0xD20C, 0x00 );
LCD_WriteReg( 0xD20D, 0xA6 );
LCD_WriteReg( 0xD20E, 0x00 );
LCD_WriteReg( 0xD20F, 0xE5 );
LCD_WriteReg( 0xD210, 0x01 );
LCD_WriteReg( 0xD211, 0x13 );
LCD_WriteReg( 0xD212, 0x01 );
LCD_WriteReg( 0xD213, 0x54 );
LCD_WriteReg( 0xD214, 0x01 );
LCD_WriteReg( 0xD215, 0x82 );
LCD_WriteReg( 0xD216, 0x01 );
LCD_WriteReg( 0xD217, 0xCA );
LCD_WriteReg( 0xD218, 0x02 );
LCD_WriteReg( 0xD219, 0x00 );
LCD_WriteReg( 0xD21A, 0x02 );
LCD_WriteReg( 0xD21B, 0x01 );
LCD_WriteReg( 0xD21C, 0x02 );
LCD_WriteReg( 0xD21D, 0x34 );
LCD_WriteReg( 0xD21E, 0x02 );
LCD_WriteReg( 0xD21F, 0x67 );
LCD_WriteReg( 0xD220, 0x02 );
LCD_WriteReg( 0xD221, 0x84 );
LCD_WriteReg( 0xD222, 0x02 );
LCD_WriteReg( 0xD223, 0xA4 );
LCD_WriteReg( 0xD224, 0x02 );
LCD_WriteReg( 0xD225, 0xB7 );
LCD_WriteReg( 0xD226, 0x02 );
LCD_WriteReg( 0xD227, 0xCF );
LCD_WriteReg( 0xD228, 0x02 );
LCD_WriteReg( 0xD229, 0xDE );
LCD_WriteReg( 0xD22A, 0x02 );
LCD_WriteReg( 0xD22B, 0xF2 );
LCD_WriteReg( 0xD22C, 0x02 );
LCD_WriteReg( 0xD22D, 0xFE );
LCD_WriteReg( 0xD22E, 0x03 );
LCD_WriteReg( 0xD22F, 0x10 );
LCD_WriteReg( 0xD230, 0x03 );
LCD_WriteReg( 0xD231, 0x33 );
LCD_WriteReg( 0xD232, 0x03 );
LCD_WriteReg( 0xD233, 0x6D );
LCD_WriteReg( 0xD300, 0x00 );
LCD_WriteReg( 0xD301, 0x33 );
LCD_WriteReg( 0xD302, 0x00 );
LCD_WriteReg( 0xD303, 0x34 );
LCD_WriteReg( 0xD304, 0x00 );
LCD_WriteReg( 0xD305, 0x3A );
LCD_WriteReg( 0xD306, 0x00 );
LCD_WriteReg( 0xD307, 0x4A );
LCD_WriteReg( 0xD308, 0x00 );
LCD_WriteReg( 0xD309, 0x5C );
LCD_WriteReg( 0xD30A, 0x00 );
LCD_WriteReg( 0xD30B, 0x81 );
LCD_WriteReg( 0xD30C, 0x00 );
LCD_WriteReg( 0xD30D, 0xA6 );
LCD_WriteReg( 0xD30E, 0x00 );
LCD_WriteReg( 0xD30F, 0xE5 );
LCD_WriteReg( 0xD310, 0x01 );
LCD_WriteReg( 0xD311, 0x13 );
LCD_WriteReg( 0xD312, 0x01 );
LCD_WriteReg( 0xD313, 0x54 );
LCD_WriteReg( 0xD314, 0x01 );
LCD_WriteReg( 0xD315, 0x82 );
LCD_WriteReg( 0xD316, 0x01 );
LCD_WriteReg( 0xD317, 0xCA );
LCD_WriteReg( 0xD318, 0x02 );
LCD_WriteReg( 0xD319, 0x00 );
LCD_WriteReg( 0xD31A, 0x02 );
LCD_WriteReg( 0xD31B, 0x01 );
LCD_WriteReg( 0xD31C, 0x02 );
LCD_WriteReg( 0xD31D, 0x34 );
LCD_WriteReg( 0xD31E, 0x02 );
LCD_WriteReg( 0xD31F, 0x67 );
LCD_WriteReg( 0xD320, 0x02 );
LCD_WriteReg( 0xD321, 0x84 );
LCD_WriteReg( 0xD322, 0x02 );
LCD_WriteReg( 0xD323, 0xA4 );
LCD_WriteReg( 0xD324, 0x02 );
LCD_WriteReg( 0xD325, 0xB7 );
LCD_WriteReg( 0xD326, 0x02 );
LCD_WriteReg( 0xD327, 0xCF );
LCD_WriteReg( 0xD328, 0x02 );
LCD_WriteReg( 0xD329, 0xDE );
LCD_WriteReg( 0xD32A, 0x02 );
LCD_WriteReg( 0xD32B, 0xF2 );
LCD_WriteReg( 0xD32C, 0x02 );
LCD_WriteReg( 0xD32D, 0xFE );
LCD_WriteReg( 0xD32E, 0x03 );
LCD_WriteReg( 0xD32F, 0x10 );
LCD_WriteReg( 0xD330, 0x03 );
LCD_WriteReg( 0xD331, 0x33 );
LCD_WriteReg( 0xD332, 0x03 );
LCD_WriteReg( 0xD333, 0x6D );
LCD_WriteReg( 0xD400, 0x00 );
LCD_WriteReg( 0xD401, 0x33 );
LCD_WriteReg( 0xD402, 0x00 );
LCD_WriteReg( 0xD403, 0x34 );
LCD_WriteReg( 0xD404, 0x00 );
LCD_WriteReg( 0xD405, 0x3A );
LCD_WriteReg( 0xD406, 0x00 );
LCD_WriteReg( 0xD407, 0x4A );
LCD_WriteReg( 0xD408, 0x00 );
LCD_WriteReg( 0xD409, 0x5C );
LCD_WriteReg( 0xD40A, 0x00 );
LCD_WriteReg( 0xD40B, 0x81 );
LCD_WriteReg( 0xD40C, 0x00 );
LCD_WriteReg( 0xD40D, 0xA6 );
LCD_WriteReg( 0xD40E, 0x00 );
LCD_WriteReg( 0xD40F, 0xE5 );
LCD_WriteReg( 0xD410, 0x01 );
LCD_WriteReg( 0xD411, 0x13 );
LCD_WriteReg( 0xD412, 0x01 );
LCD_WriteReg( 0xD413, 0x54 );
LCD_WriteReg( 0xD414, 0x01 );
LCD_WriteReg( 0xD415, 0x82 );
LCD_WriteReg( 0xD416, 0x01 );
LCD_WriteReg( 0xD417, 0xCA );
LCD_WriteReg( 0xD418, 0x02 );
LCD_WriteReg( 0xD419, 0x00 );
LCD_WriteReg( 0xD41A, 0x02 );
LCD_WriteReg( 0xD41B, 0x01 );
LCD_WriteReg( 0xD41C, 0x02 );
LCD_WriteReg( 0xD41D, 0x34 );
LCD_WriteReg( 0xD41E, 0x02 );
LCD_WriteReg( 0xD41F, 0x67 );
LCD_WriteReg( 0xD420, 0x02 );
LCD_WriteReg( 0xD421, 0x84 );
LCD_WriteReg( 0xD422, 0x02 );
LCD_WriteReg( 0xD423, 0xA4 );
LCD_WriteReg( 0xD424, 0x02 );
LCD_WriteReg( 0xD425, 0xB7 );
LCD_WriteReg( 0xD426, 0x02 );
LCD_WriteReg( 0xD427, 0xCF );
LCD_WriteReg( 0xD428, 0x02 );
LCD_WriteReg( 0xD429, 0xDE );
LCD_WriteReg( 0xD42A, 0x02 );
LCD_WriteReg( 0xD42B, 0xF2 );
LCD_WriteReg( 0xD42C, 0x02 );
LCD_WriteReg( 0xD42D, 0xFE );
LCD_WriteReg( 0xD42E, 0x03 );
LCD_WriteReg( 0xD42F, 0x10 );
LCD_WriteReg( 0xD430, 0x03 );
LCD_WriteReg( 0xD431, 0x33 );
LCD_WriteReg( 0xD432, 0x03 );
LCD_WriteReg( 0xD433, 0x6D );
LCD_WriteReg( 0xD500, 0x00 );
LCD_WriteReg( 0xD501, 0x33 );
LCD_WriteReg( 0xD502, 0x00 );
LCD_WriteReg( 0xD503, 0x34 );
LCD_WriteReg( 0xD504, 0x00 );
LCD_WriteReg( 0xD505, 0x3A );
LCD_WriteReg( 0xD506, 0x00 );
LCD_WriteReg( 0xD507, 0x4A );
LCD_WriteReg( 0xD508, 0x00 );
LCD_WriteReg( 0xD509, 0x5C );
LCD_WriteReg( 0xD50A, 0x00 );
LCD_WriteReg( 0xD50B, 0x81 );
LCD_WriteReg( 0xD50C, 0x00 );
LCD_WriteReg( 0xD50D, 0xA6 );
LCD_WriteReg( 0xD50E, 0x00 );
LCD_WriteReg( 0xD50F, 0xE5 );
LCD_WriteReg( 0xD510, 0x01 );
LCD_WriteReg( 0xD511, 0x13 );
LCD_WriteReg( 0xD512, 0x01 );
LCD_WriteReg( 0xD513, 0x54 );
LCD_WriteReg( 0xD514, 0x01 );
LCD_WriteReg( 0xD515, 0x82 );
LCD_WriteReg( 0xD516, 0x01 );
LCD_WriteReg( 0xD517, 0xCA );
LCD_WriteReg( 0xD518, 0x02 );
LCD_WriteReg( 0xD519, 0x00 );
LCD_WriteReg( 0xD51A, 0x02 );
LCD_WriteReg( 0xD51B, 0x01 );
LCD_WriteReg( 0xD51C, 0x02 );
LCD_WriteReg( 0xD51D, 0x34 );
LCD_WriteReg( 0xD51E, 0x02 );
LCD_WriteReg( 0xD51F, 0x67 );
LCD_WriteReg( 0xD520, 0x02 );
LCD_WriteReg( 0xD521, 0x84 );
LCD_WriteReg( 0xD522, 0x02 );
LCD_WriteReg( 0xD523, 0xA4 );
LCD_WriteReg( 0xD524, 0x02 );
LCD_WriteReg( 0xD525, 0xB7 );
LCD_WriteReg( 0xD526, 0x02 );
LCD_WriteReg( 0xD527, 0xCF );
LCD_WriteReg( 0xD528, 0x02 );
LCD_WriteReg( 0xD529, 0xDE );
LCD_WriteReg( 0xD52A, 0x02 );
LCD_WriteReg( 0xD52B, 0xF2 );
LCD_WriteReg( 0xD52C, 0x02 );
LCD_WriteReg( 0xD52D, 0xFE );
LCD_WriteReg( 0xD52E, 0x03 );
LCD_WriteReg( 0xD52F, 0x10 );
LCD_WriteReg( 0xD530, 0x03 );
LCD_WriteReg( 0xD531, 0x33 );
LCD_WriteReg( 0xD532, 0x03 );
LCD_WriteReg( 0xD533, 0x6D );
LCD_WriteReg( 0xD600, 0x00 );
LCD_WriteReg( 0xD601, 0x33 );
LCD_WriteReg( 0xD602, 0x00 );
LCD_WriteReg( 0xD603, 0x34 );
LCD_WriteReg( 0xD604, 0x00 );
LCD_WriteReg( 0xD605, 0x3A );
LCD_WriteReg( 0xD606, 0x00 );
LCD_WriteReg( 0xD607, 0x4A );
LCD_WriteReg( 0xD608, 0x00 );
LCD_WriteReg( 0xD609, 0x5C );
LCD_WriteReg( 0xD60A, 0x00 );
LCD_WriteReg( 0xD60B, 0x81 );
LCD_WriteReg( 0xD60C, 0x00 );
LCD_WriteReg( 0xD60D, 0xA6 );
LCD_WriteReg( 0xD60E, 0x00 );
LCD_WriteReg( 0xD60F, 0xE5 );
LCD_WriteReg( 0xD610, 0x01 );
LCD_WriteReg( 0xD611, 0x13 );
LCD_WriteReg( 0xD612, 0x01 );
LCD_WriteReg( 0xD613, 0x54 );
LCD_WriteReg( 0xD614, 0x01 );
LCD_WriteReg( 0xD615, 0x82 );
LCD_WriteReg( 0xD616, 0x01 );
LCD_WriteReg( 0xD617, 0xCA );
LCD_WriteReg( 0xD618, 0x02 );
LCD_WriteReg( 0xD619, 0x00 );
LCD_WriteReg( 0xD61A, 0x02 );
LCD_WriteReg( 0xD61B, 0x01 );
LCD_WriteReg( 0xD61C, 0x02 );
LCD_WriteReg( 0xD61D, 0x34 );
LCD_WriteReg( 0xD61E, 0x02 );
LCD_WriteReg( 0xD61F, 0x67 );
LCD_WriteReg( 0xD620, 0x02 );
LCD_WriteReg( 0xD621, 0x84 );
LCD_WriteReg( 0xD622, 0x02 );
LCD_WriteReg( 0xD623, 0xA4 );
LCD_WriteReg( 0xD624, 0x02 );
LCD_WriteReg( 0xD625, 0xB7 );
LCD_WriteReg( 0xD626, 0x02 );
LCD_WriteReg( 0xD627, 0xCF );
LCD_WriteReg( 0xD628, 0x02 );
LCD_WriteReg( 0xD629, 0xDE );
LCD_WriteReg( 0xD62A, 0x02 );
LCD_WriteReg( 0xD62B, 0xF2 );
LCD_WriteReg( 0xD62C, 0x02 );
LCD_WriteReg( 0xD62D, 0xFE );
LCD_WriteReg( 0xD62E, 0x03 );
LCD_WriteReg( 0xD62F, 0x10 );
LCD_WriteReg( 0xD630, 0x03 );
LCD_WriteReg( 0xD631, 0x33 );
LCD_WriteReg( 0xD632, 0x03 );
LCD_WriteReg( 0xD633, 0x6D );
//LV2 Page 0 enable
LCD_WriteReg( 0xF000, 0x55 );
LCD_WriteReg( 0xF001, 0xAA );
LCD_WriteReg( 0xF002, 0x52 );
LCD_WriteReg( 0xF003, 0x08 );
LCD_WriteReg( 0xF004, 0x00 );
//Display control
LCD_WriteReg( 0xB100, 0xCC );
LCD_WriteReg( 0xB101, 0x00 );
//Source hold time
LCD_WriteReg( 0xB600, 0x05 );
//Gate EQ control
LCD_WriteReg( 0xB700, 0x70 );
LCD_WriteReg( 0xB701, 0x70 );
//Source EQ control (Mode 2)
LCD_WriteReg( 0xB800, 0x01 );
LCD_WriteReg( 0xB801, 0x03 );
LCD_WriteReg( 0xB802, 0x03 );
LCD_WriteReg( 0xB803, 0x03 );
//Inversion mode (2-dot)
LCD_WriteReg( 0xBC00, 0x05 );
LCD_WriteReg( 0xBC01, 0x00 );
LCD_WriteReg( 0xBC02, 0x00 );
//Timing control 4H w/ 4-delay
LCD_WriteReg( 0xC900, 0x11 );
LCD_WriteReg( 0xC901, 0x02 );
LCD_WriteReg( 0xC902, 0x50 );
LCD_WriteReg( 0xC903, 0x50 );
LCD_WriteReg( 0xC904, 0x50 );
LCD_WriteReg( 0x3500, 0x00 );
LCD_WriteReg( 0x3A00, 0x55 ); //16-bit/pixel
LCD_WR_REG( 0x2100 );
LCD_WR_REG( 0x1100 );
delay_us( 120 );
LCD_WR_REG( 0x2900 );
}
LCD_Display_Dir( 1 ); //0竖屏
LCD_Clear( GBLUE );
LCD_Draw_Circle( 200, 200, 100, RED );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", RED, GBLUE );
}
/**************************************************************
* @Name bsp_NT35510_Gpio_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
static void bsp_NT35510_Gpio_Init( void )
{
stc_gpio_init_t stcGpioInit;
LL_PERIPH_WE( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM );
/* Init LCD backlight IO */
GPIO_StructInit( &stcGpioInit );
stcGpioInit.u16PinDrv = PIN_HIGH_DRV;
stcGpioInit.u16PinDir = PIN_DIR_OUT;
stcGpioInit.u16PullUp = PIN_PU_ON;
GPIO_Init( LCD_BKL_PORT, LCD_BKL_PIN, &stcGpioInit );
GPIO_SetPins( LCD_BKL_PORT, LCD_BKL_PIN );
/* Config xLCD_RST */
TCA9539_WritePin( TCA9539_IO_PORT0, TCA9539_IO_PIN7, TCA9539_PIN_SET );
TCA9539_ConfigPin( TCA9539_IO_PORT0, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
/************************* Set pin drive capacity *************************/
GPIO_StructInit( &stcGpioInit );
stcGpioInit.u16PinDrv = PIN_HIGH_DRV;
/* SMC_CS SMC_WE SMC_OE */
GPIO_Init( LCD_CS_PORT, LCD_CS_PIN, &stcGpioInit );
GPIO_Init( LCD_WE_PORT, LCD_WE_PIN, &stcGpioInit );
GPIO_Init( LCD_OE_PORT, LCD_OE_PIN, &stcGpioInit );
/* SMC_ADD[0:18]*/
GPIO_Init( LCD_ADD12_PORT, LCD_ADD12_PIN, &stcGpioInit );
/* SMC_DATA[0:15] */
GPIO_Init( LCD_DATA0_PORT, LCD_DATA0_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA1_PORT, LCD_DATA1_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA2_PORT, LCD_DATA2_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA3_PORT, LCD_DATA3_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA4_PORT, LCD_DATA4_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA5_PORT, LCD_DATA5_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA6_PORT, LCD_DATA6_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA7_PORT, LCD_DATA7_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA8_PORT, LCD_DATA8_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA9_PORT, LCD_DATA9_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA10_PORT, LCD_DATA10_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA11_PORT, LCD_DATA11_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA12_PORT, LCD_DATA12_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA13_PORT, LCD_DATA13_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA14_PORT, LCD_DATA14_PIN, &stcGpioInit );
GPIO_Init( LCD_DATA15_PORT, LCD_DATA15_PIN, &stcGpioInit );
/************************** Set EXMC pin function *************************/
/* SMC_DATA[0:15] */
GPIO_SetFunc( LCD_DATA0_PORT, LCD_DATA0_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA1_PORT, LCD_DATA1_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA2_PORT, LCD_DATA2_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA3_PORT, LCD_DATA3_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA4_PORT, LCD_DATA4_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA5_PORT, LCD_DATA5_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA6_PORT, LCD_DATA6_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA7_PORT, LCD_DATA7_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA8_PORT, LCD_DATA8_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA9_PORT, LCD_DATA9_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA10_PORT, LCD_DATA10_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA11_PORT, LCD_DATA11_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA12_PORT, LCD_DATA12_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA13_PORT, LCD_DATA13_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA14_PORT, LCD_DATA14_PIN, GPIO_FUNC_12 );
GPIO_SetFunc( LCD_DATA15_PORT, LCD_DATA15_PIN, GPIO_FUNC_12 );
/* SMC_CS */
GPIO_SetFunc( LCD_CS_PORT, LCD_CS_PIN, GPIO_FUNC_12 );
/* SMC_WE */
GPIO_SetFunc( LCD_WE_PORT, LCD_WE_PIN, GPIO_FUNC_12 );
/* SMC_OE */
GPIO_SetFunc( LCD_OE_PORT, LCD_OE_PIN, GPIO_FUNC_12 );
/* SMC_ADD[0:18]*/
GPIO_SetFunc( LCD_ADD12_PORT, LCD_ADD12_PIN, GPIO_FUNC_12 );
LL_PERIPH_WP( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU | \
LL_PERIPH_EFM | LL_PERIPH_SRAM );
LCD_RSTCmd( 0 );
delay_ms( 50 );
LCD_RSTCmd( 1 );
delay_ms( 50 );
}
/**************************************************************
* @Name bsp_NT35510_EXMC_Init
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
static int32_t bsp_NT35510_EXMC_Init( void )
{
__IO uint32_t u32To = 0UL;
int32_t i32Ret = LL_OK;
stc_exmc_smc_init_t stcSmcInit;
stc_exmc_smc_chip_config_t stcChipConfig;
stc_exmc_smc_timing_config_t stcTimingConfig;
en_flag_status_t enChipStatus = RESET;
en_flag_status_t enTimingStatus = RESET;
LL_PERIPH_WE( LL_PERIPH_FCG );
FCG_Fcg3PeriphClockCmd( FCG3_PERIPH_SMC, ENABLE );
EXMC_SMC_Cmd( ENABLE );
EXMC_SMC_ExitLowPower();
while ( EXMC_SMC_READY != EXMC_SMC_GetStatus() )
{
;
}
/* Configure SMC width && CS &chip & timing. */
EXMC_SMC_StructInit( &stcSmcInit );
stcSmcInit.stcChipConfig.u32AddrMatch = 0x60UL;//?
stcSmcInit.stcChipConfig.u32AddrMask = EXMC_SMC_ADDR_MASK_16MB;
stcSmcInit.stcChipConfig.u32MemoryWidth = EXMC_SMC_MEMORY_WIDTH_16BIT;
stcSmcInit.stcChipConfig.u32BAA = EXMC_SMC_BAA_PORT_DISABLE;
stcSmcInit.stcChipConfig.u32ADV = EXMC_SMC_ADV_PORT_DISABLE;
stcSmcInit.stcChipConfig.u32BLS = EXMC_SMC_BLS_SYNC_CS;
stcSmcInit.stcChipConfig.u32ReadBurstLen = EXMC_SMC_READ_BURST_4BEAT;
stcSmcInit.stcChipConfig.u32WriteBurstLen = EXMC_SMC_WRITE_BURST_4BEAT;
stcSmcInit.stcChipConfig.u32ReadMode = EXMC_SMC_READ_SYNC;
stcSmcInit.stcChipConfig.u32WriteMode = EXMC_SMC_WRITE_SYNC;
stcSmcInit.stcTimingConfig.u8RC = 6U;
stcSmcInit.stcTimingConfig.u8WC = 4U;
stcSmcInit.stcTimingConfig.u8CEOE = 1U;
stcSmcInit.stcTimingConfig.u8WP = 2U;
stcSmcInit.stcTimingConfig.u8PC = 0U;//4
stcSmcInit.stcTimingConfig.u8TR = 0U;//1
EXMC_SMC_Init( EXMC_SMC_CHIP3, &stcSmcInit );
/* Set command: updateregs */
EXMC_SMC_SetCommand( EXMC_SMC_CHIP3, EXMC_SMC_CMD_UPDATEREGS, 0UL, 0UL );
/* Check timing status */
u32To = 0UL;
while ( ( enChipStatus != SET ) || ( enTimingStatus != SET ) )
{
( void )EXMC_SMC_GetTimingConfig( EXMC_SMC_CHIP3, &stcTimingConfig );
if ( 0 == memcmp( &stcTimingConfig, &stcSmcInit.stcTimingConfig, sizeof( stcTimingConfig ) ) )
{
enTimingStatus = SET;
}
EXMC_SMC_GetChipConfig( EXMC_SMC_CHIP3, &stcChipConfig );
if ( 0 == memcmp( &stcChipConfig, &stcSmcInit.stcChipConfig, sizeof( stcChipConfig ) ) )
{
enChipStatus = SET;
}
if ( u32To > SMC_MAX_TIMEOUT )
{
i32Ret = LL_ERR;
break;
}
u32To++;
}
LL_PERIPH_WP( LL_PERIPH_FCG );
return i32Ret;
}
以下是液晶屏寄存器读写代码:
typedef struct
{
__IO uint16_t LCD_REG;
__IO uint16_t LCD_RAM;
} NT35510_TypeDef;
/* --- Define ------------------------------------------------------------*/
#define LCD_BASE ( ( uint32_t )( 0x60000000 | 0x00001FFE ) )
#define LCD ( ( NT35510_TypeDef * ) LCD_BASE )
/**************************************************************
* @Name LCD_WR_REG
* @brief 写寄存器函数
* @param regval: [输入/出] 寄存器值
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_WR_REG( __IO uint16_t regval )
{
regval = regval; //使用-O2优化的时候,必须插入的延时
LCD->LCD_REG = regval;//写入要写的寄存器序号
}
/**************************************************************
* @Name LCD_WR_DATA
* @brief 写LCD数据
* @param data: [输入/出] 要写入的值
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_WR_DATA( __IO uint16_t data )
{
data = data; //使用-O2优化的时候,必须插入的延时
LCD->LCD_RAM = data;
}
/**************************************************************
* @Name LCD_RD_DATA
* @brief 读LCD数据
* @param None
* @retval 读到的值
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint16_t LCD_RD_DATA( void )
{
__IO uint16_t ram; //防止被优化
ram = LCD->LCD_RAM;
return ram;
}
/**************************************************************
* @Name LCD_WriteReg
* @brief 写寄存器
* @param LCD_Reg: [输入/出] 寄存器地址
** LCD_RegValue: [输入/出] 要写入的数据
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_WriteReg( uint16_t LCD_Reg, uint16_t LCD_RegValue )
{
LCD->LCD_REG = LCD_Reg; //写入要写的寄存器序号
LCD->LCD_RAM = LCD_RegValue;//写入数据
}
/**************************************************************
* @Name LCD_ReadReg
* @brief 读寄存器
* @param LCD_Reg: [输入/出] 寄存器地址
* @retval 读到的数据
* @author Zachary
* @Data 2022-11-09
**************************************************************/
uint16_t LCD_ReadReg( uint16_t LCD_Reg )
{
LCD_WR_REG( LCD_Reg ); //写入要读的寄存器序号
// delay_us(5);
return LCD_RD_DATA(); //返回读到的值
}
以下是部分液晶操作函数,主要是画点、清屏、画圆等函数:
/**************************************************************
* @Name LCD_Clear
* @brief 清屏函数
* @param color: [输入/出] 要清屏的填充色
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_Clear( uint16_t color )
{
uint32_t index = 0;
uint32_t totalpoint = lcddev.width;
totalpoint *= lcddev.height; //得到总点数
LCD_SetCursor( 0x00, 0x0000 ); //设置光标位置
LCD->LCD_REG = lcddev.wramcmd; //开始写入GRAM
for ( index = 0; index < totalpoint; index++ )
{
LCD->LCD_RAM = color;
}
}
/**************************************************************
* @Name LCD_Draw_Circle
* @brief 在指定位置画一个指定大小的圆
* @param x0: [输入/出]
** y0: [输入/出] 中心点
** r: [输入/出] 半径
** color: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_Draw_Circle( uint16_t x0, uint16_t y0, uint8_t r, uint16_t color )
{
int a, b;
int di;
a = 0;
b = r;
di = 3 - ( r << 1 ); //判断下个点位置的标志
while ( a <= b )
{
LCD_DrawPoint( x0 + a, y0 - b, color ); //5
LCD_DrawPoint( x0 + b, y0 - a, color ); //0
LCD_DrawPoint( x0 + b, y0 + a, color ); //4
LCD_DrawPoint( x0 + a, y0 + b, color ); //6
LCD_DrawPoint( x0 - a, y0 + b, color ); //1
LCD_DrawPoint( x0 - b, y0 + a, color );
LCD_DrawPoint( x0 - a, y0 - b, color ); //2
LCD_DrawPoint( x0 - b, y0 - a, color ); //7
a++;
//使用Bresenham算法画圆
if ( di < 0 )di += 4 * a + 6;
else
{
di += 10 + 4 * ( a - b );
b--;
}
}
}
/**************************************************************
* @Name LCD_ShowChar
* @brief 在指定位置显示一个字符
* @param x: [输入/出]
** y: [输入/出] 起始坐标
** num: [输入/出] 要显示的字符
** size: [输入/出] 字体大小
** mode: [输入/出] 叠加方式
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_ShowChar( uint16_t x, uint16_t y, uint8_t num, uint8_t size, uint8_t mode, uint16_t frontcolor, uint16_t backcolor )
{
uint8_t temp, t1, t;
uint16_t y0 = y;
uint8_t csize = ( size / 8 + ( ( size % 8 ) ? 1 : 0 ) ) * ( size / 2 ); //得到字体一个字符对应点阵集所占的字节数
num = num - ' '; //得到偏移后的值(ASCII字库是从空格开始取模,所以-' '就是对应字符的字库)
for( t = 0; t < csize; t++ )
{
if( size == 12 )
temp = asc2_1206[num][t]; //调用1206字体
else if( size == 16 )
temp = asc2_1608[num][t]; //调用1608字体
else if( size == 24 )
temp = asc2_2412[num][t]; //调用2412字体
else
return; //没有的字库
for( t1 = 0; t1 < 8; t1++ )
{
if( temp & 0x80 )
LCD_Fast_DrawPoint( x, y, frontcolor );
else if( mode == 0 )
LCD_Fast_DrawPoint( x, y, backcolor );
temp <<= 1;
y++;
if( y >= lcddev.height )
return; //超区域了
if( ( y - y0 ) == size )
{
y = y0;
x++;
if( x >= lcddev.width )
return; //超区域了
break;
}
}
}
}
/**************************************************************
* @Name LCD_ShowString
* @brief 显示字符串
* @param x: [输入/出]
** y: [输入/出] 起点坐标
** width: [输入/出]
** height: [输入/出] 区域大小
** size: [输入/出] 字体大小
** p: [输入/出] 字符串起始地址
** frontcolor: [输入/出]
** backcolor: [输入/出]
* @retval
* @author Zachary
* @Data 2022-11-09
**************************************************************/
void LCD_ShowString( uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t size, uint8_t *p, uint16_t frontcolor, uint16_t backcolor )
{
uint8_t x0 = x;
width += x;
height += y;
while ( ( *p <= '~' ) && ( *p >= ' ' ) ) //判断是不是非法字符!
{
if ( x >= width )
{
x = x0;
y += size;
}
if ( y >= height )
break; //退出
LCD_ShowChar( x, y, *p, size, 0, frontcolor, backcolor );
x += size / 2;
p++;
}
}
以下是刷屏测试和字符显示代码:
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-02-13
**************************************************************/
int32_t main( void )
{
SysInit();
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
delay_ms( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
delay_ms( 500 );
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
bsp_NT35510_Init();
LCD_Test();
}
/**************************************************************
* @Name LCD_Test
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-03-04
**************************************************************/
static void LCD_Test( void )
{
LCD_Clear( RED );
delay_ms( 500 );
LCD_Clear( GREEN );
delay_ms( 500 );
LCD_Clear( BLACK );
delay_ms( 500 );
LCD_Clear( BLUE );
delay_ms( 500 );
LCD_Clear( YELLOW );
delay_ms( 500 );
LCD_Clear( MAGENTA );
delay_ms( 500 );
LCD_Draw_Circle( 200, 200, 100, BLACK );
LCD_ShowString( 300, 300, lcddev.width, lcddev.height, 24, ( uint8_t * )"hello world", BLACK, MAGENTA );
}
以下是测试视频:
[localvideo]2555bd254705abb4abab10eacc694bc9[/localvideo]
以下是代码附件:
- 2023-03-04
-
发表了主题帖:
HC32F4A0-I2C应用
HC32F4A0-Keil环境搭建与点灯 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
HC32F4A0-串口中断接收与中断发送 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
上篇讲了串口及其中断的使用,本篇主要是使用芯片内部I2C控制器对TCA9539这个IO扩展芯片进行控制,使LED点亮。也方便下篇点亮LCD液晶和触摸屏做准备,液晶屏的复位管脚和触摸的中断管脚使用了TCA9539扩展出来的IO。
以下是板载TCA9539的硬件原理图和TCA9539地址确认图:
从上图可知,TCA9539的A0、A1管脚接地,TCA9539的第3至第7位是固定值,第0位位读写控制位,只有第1、2位可有硬件设置,由此可知TCA9539的从机地址位0x74。且TCA9539在一条I2C总线上最多只能由4片。
以下是HC32F4A0的时序图:
从该时序图中可知,通过开始条件、地址传送、数据传送、停止条件这几个步骤,即可完成一次正常通信,TCA9539的驱动代码也是严格按照这个顺序进行的。以下是TCA9539的读写时序图;对比代码和时序图,可以帮我们更好的理解HC32F4A0的I2C的使用。
以下是I2C的配置代码:
#define TCA9539_I2C_FCG_ENABLE() FCG_Fcg1PeriphClockCmd( FCG1_PERIPH_I2C1, ENABLE )
#define TCA9539_I2C_SCL_PIN GPIO_PIN_03
#define TCA9539_I2C_SCL_PORT GPIO_PORT_D
#define TCA9539_I2C_SDA_PIN GPIO_PIN_10
#define TCA9539_I2C_SDA_PORT GPIO_PORT_F
#define TCA9539_INT_PIN GPIO_PIN_01
#define TCA9539_INT_PORT GPIO_PORT_I
#define TCA9539_RST_PIN GPIO_PIN_13
#define TCA9539_RST_PORT GPIO_PORT_C
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] bsp_TCA9539_Init
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-02-19
**************************************************************/
void bsp_TCA9539_Init( void )
{
float32_t fErr;
int32_t i32Ret = LL_ERR;
stc_gpio_init_t GPIO_InitStruct = { 0 };
stc_i2c_init_t I2C_InitStruct = { 0 };
LL_PERIPH_WE( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
GPIO_ResetPins( TCA9539_RST_PORT, TCA9539_RST_PIN );
GPIO_StructInit( &GPIO_InitStruct );
GPIO_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
GPIO_InitStruct.u16PinOutputType = PIN_OUT_TYPE_NMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
GPIO_Init( TCA9539_RST_PORT, TCA9539_RST_PIN, &GPIO_InitStruct );
delay_ms( 5 );
GPIO_SetPins( TCA9539_RST_PORT, TCA9539_RST_PIN );
GPIO_StructInit( &GPIO_InitStruct );
GPIO_Init( TCA9539_I2C_SCL_PORT, TCA9539_I2C_SCL_PIN, &GPIO_InitStruct );
GPIO_Init( TCA9539_I2C_SDA_PORT, TCA9539_I2C_SDA_PIN, &GPIO_InitStruct );
GPIO_SetFunc( TCA9539_I2C_SCL_PORT, TCA9539_I2C_SCL_PIN, GPIO_FUNC_49 );
GPIO_SetFunc( TCA9539_I2C_SDA_PORT, TCA9539_I2C_SDA_PIN, GPIO_FUNC_48 );
TCA9539_I2C_FCG_ENABLE();
I2C_InitStruct.u32Baudrate = 100000UL;
I2C_InitStruct.u32ClockDiv = I2C_CLK_DIV16;
I2C_InitStruct.u32SclTime = 0UL;
for ( uint8_t i = 0U; i < 5U; i++ )
{
i32Ret = I2C_Init( TCA9539_I2Cx, &I2C_InitStruct, &fErr );
if ( LL_OK != i32Ret )
{
I2C_InitStruct.u32ClockDiv--;
}
else
{
break;
}
}
if ( LL_OK == i32Ret )
{
I2C_BusWaitCmd( TCA9539_I2Cx, ENABLE );
}
I2C_Cmd( TCA9539_I2Cx, ENABLE );
LL_PERIPH_WP( LL_PERIPH_GPIO | LL_PERIPH_FCG | LL_PERIPH_PWC_CLK_RMU );
}
以下是使用I2C读写TCA9539的代码,根据代码联系上述读写时序图,可以清楚的理解该外设的使用,代码均有注释,可以看出是一步步按照时序图走的。
/**************************************************************
* @Name TCA9539_I2C_Write
* @brief
* @param u16DevAddr: [输入/出]
** pu8Reg: [输入/出]
** u8RegLen: [输入/出]
** pu8Buf: [输入/出]
** u32Len: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_I2C_Write( uint16_t u16DevAddr, const uint8_t *pu8Reg, uint8_t u8RegLen, const uint8_t *pu8Buf, uint32_t u32Len )
{
int32_t i32Ret;
I2C_SWResetCmd( TCA9539_I2Cx, ENABLE );
I2C_SWResetCmd( TCA9539_I2Cx, DISABLE );
i32Ret = I2C_Start( TCA9539_I2Cx, I2C_TIMEOUT ); /* ---起始信号--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransAddr( TCA9539_I2Cx, u16DevAddr, I2C_DIR_TX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( TCA9539_I2Cx, pu8Reg, u8RegLen, I2C_TIMEOUT ); /* ---TCA9539寄存器地址传送--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( TCA9539_I2Cx, pu8Buf, u32Len, I2C_TIMEOUT ); /* ---数据传送--- */
}
}
}
I2C_Stop( TCA9539_I2Cx, I2C_TIMEOUT );
}
/**************************************************************
* @Name TCA9539_I2C_Read
* @brief
* @param u16DevAddr: [输入/出]
** pu8Reg: [输入/出]
** u8RegLen: [输入/出]
** pu8Buf: [输入/出]
** u32Len: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_I2C_Read( uint16_t u16DevAddr, const uint8_t *pu8Reg, uint8_t u8RegLen, uint8_t *pu8Buf, uint32_t u32Len )
{
int32_t i32Ret;
I2C_SWResetCmd( TCA9539_I2Cx, ENABLE );
I2C_SWResetCmd( TCA9539_I2Cx, DISABLE );
i32Ret = I2C_Start( TCA9539_I2Cx, I2C_TIMEOUT ); /* ---起始信号--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransAddr( TCA9539_I2Cx, u16DevAddr, I2C_DIR_TX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_TransData( TCA9539_I2Cx, pu8Reg, u8RegLen, I2C_TIMEOUT ); /* ---TCA9539寄存器地址传送--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_Restart( TCA9539_I2Cx, I2C_TIMEOUT ); /* ---重新开始--- */
if ( LL_OK == i32Ret )
{
if ( 1UL == u32Len )
{
I2C_AckConfig( TCA9539_I2Cx, I2C_NACK );
}
i32Ret = I2C_TransAddr( TCA9539_I2Cx, u16DevAddr, I2C_DIR_RX, I2C_TIMEOUT ); /* ---7位从机地址传送+传输方向--- */
if ( LL_OK == i32Ret )
{
i32Ret = I2C_MasterReceiveDataAndStop( TCA9539_I2Cx, pu8Buf, u32Len, I2C_TIMEOUT ); /* ---开始数据接收--- */
}
I2C_AckConfig( TCA9539_I2Cx, I2C_ACK );
}
}
}
}
if ( LL_OK != i32Ret )
{
( void )I2C_Stop( TCA9539_I2Cx, I2C_TIMEOUT );
}
}
以下是TCA9539的IO配置代码和IO口读写代码,可以将IO口配置位输出或输入:
/**************************************************************
* @Name TCA9539_WritePin
* @brief
* @param u8Port: [输入/出]
** u8Pin: [输入/出]
** u8PinState: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_WritePin( uint8_t u8Port, uint8_t u8Pin, uint8_t u8PinState )
{
uint8_t u8TempData[2];
u8TempData[0] = u8Port + TCA9539_REG_OUTPUT_PORT0;
TCA9539_I2C_Read( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
if ( 0U == u8PinState )
{
u8TempData[1] &= ( uint8_t )( ~u8Pin );
}
else
{
u8TempData[1] |= u8Pin;
}
TCA9539_I2C_Write( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
}
/**************************************************************
* @Name TCA9539_ReadPin
* @brief
* @param u8Port: [输入/出]
** u8Pin: [输入/出]
** pu8PinState: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_ReadPin( uint8_t u8Port, uint8_t u8Pin, uint8_t *pu8PinState )
{
uint8_t u8TempData[2];
u8TempData[0] = u8Port + TCA9539_REG_INPUT_PORT0;
TCA9539_I2C_Read( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
if (0U != (u8TempData[1] & u8Pin))
{
*pu8PinState = 0x01U;
}
else
{
*pu8PinState = 0x00U;
}
}
/**************************************************************
* @Name TCA9539_ConfigPin
* @brief
* @param u8Port: [输入/出]
** u8Pin: [输入/出]
** u8Dir: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-19
**************************************************************/
void TCA9539_ConfigPin( uint8_t u8Port, uint8_t u8Pin, uint8_t u8Dir)
{
uint8_t u8TempData[2];
u8TempData[0] = u8Port + TCA9539_REG_CONFIG_PORT0;
TCA9539_I2C_Read( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
if ( TCA9539_DIR_OUT == u8Dir )
{
u8TempData[1] &= (uint8_t)(~u8Pin);
}
else
{
u8TempData[1] |= u8Pin;
}
TCA9539_I2C_Write( TCA9539_I2C_ADDR, &u8TempData[0], 1U, &u8TempData[1], 1U );
}
以下是main.c测试代码,我们将板载LED2所对应的管脚配置为输出,并且在主循环中每500ms反转一次。
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_TCA9539_Init();
TCA9539_ConfigPin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_DIR_OUT );
}
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-02-13
**************************************************************/
int32_t main( void )
{
SysInit();
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_SET );
delay_ms( 500 );
TCA9539_WritePin( TCA9539_IO_PORT1, TCA9539_IO_PIN7, TCA9539_PIN_RESET );
delay_ms( 500 );
}
}
以下是测试视频:
[localvideo]c82d0cd3776ebd6ba312c55846f42f8d[/localvideo]
以下是代码附件:
- 2023-02-25
-
发表了主题帖:
HC32F4A0-串口中断接收与中断发送
上篇HC32F4A0开发板内容:HC32F4A0-Keil环境搭建与点灯 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn)
根据官方手册可知,HC32F4A0SITB这款芯片具有10个串口资源,本次测试只用到了USART1。测试内容是,使用串口调试助手以1ms间隔向单片机发送数据,随后单片机上电,查看单片机串口是否会有问题,由于板载没有485,没办法将设备挂在总线上测试。曾经,485总线上有数据,设备上电时,串口初始化顺序问题,导致单片机串口卡死过,进了错误中断,因此想测试下,竟然没有485电路。
使用的是USART1,对用开发板的管脚是USART1_TX_PIN是PH15,USART1_RX_PIN是PH13;首先我们需要对串口进行管脚配置,其次是管脚功能复用,第三是串口参数配置,最后是中断配置。在手册中可以知道,PH15和PH13的通讯复用位FG1(Func32~63),那个表格太难看了,字太小了,然后再去看Func32~63这个表格,可以对应USART1_TX是Func32,USART1_RX是Func33;说实话这个看起来有点繁琐,不想其他手册,一目了然。不光如此,程序中还没有定义好,像ST或GD直接是一目了然,这个固件库直接是GPIO_FUNC_33,但凡名字换成USART1_AF_xx这种,我也不至于去查手册。
接着是串口中断配置,这个配置也是与ST或GD大不相同,hc32f4a0.h中定义了大量的中断号,我们可以随意使用,但也不是每个都能用,具体需要查看表格,下表是USART1的中断可使用的中断号表格。这里HC32F4A0这个单片机中断都是单独分开的,接收、发送、错误等中断都需要单独配置。
以下是USART1的串口配置代码,代码均有注释:
/**************************************************************
* [url=home.php?mod=space&uid=32621]@name[/url] bsp_Usart_Init
* [url=home.php?mod=space&uid=159083]@brief[/url] * @param None
* @retval
* [url=home.php?mod=space&uid=1315547]@author[/url] Zachary
* [url=home.php?mod=space&uid=34591]@data[/url] 2023-02-22
**************************************************************/
void bsp_Usart_Init( void )
{
stc_gpio_init_t Gpio_InitStruct;
stc_usart_uart_init_t Usart_InitStruct;
stc_irq_signin_config_t stcIrqSigninConfig;
LL_PERIPH_WE( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---解保护--- */
Gpio_InitStruct.u16PinDir = PIN_DIR_OUT; /* --方向 输入或输出--- */
Gpio_InitStruct.u16PinDrv = PIN_HIGH_DRV; /* --驱动能力--- */
Gpio_InitStruct.u16PinAttr = PIN_ATTR_DIGITAL; /* --数字量或模拟量--- */
Gpio_InitStruct.u16PinOutputType = PIN_OUT_TYPE_CMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
Gpio_InitStruct.u16PinState = PIN_STAT_SET; /* --初始化后电平状态--- */
Gpio_InitStruct.u16PullUp = PIN_PU_ON; /* --上拉输出--- */
Gpio_InitStruct.u16ExtInt = PIN_EXTINT_OFF; /* --中断功能不开启--- */
Gpio_InitStruct.u16Invert = PIN_INVT_OFF; /* --反向功能不开启--- */
Gpio_InitStruct.u16Latch = PIN_LATCH_OFF; /* --输出锁存功能不开启--- */
GPIO_Init( USART1_TX_PORT, USART1_TX_PIN, &Gpio_InitStruct );
Gpio_InitStruct.u16PinDir = PIN_DIR_IN; /* --方向 输入或输出--- */
Gpio_InitStruct.u16PinDrv = PIN_HIGH_DRV; /* --驱动能力--- */
Gpio_InitStruct.u16PinAttr = PIN_ATTR_DIGITAL; /* --数字量或模拟量--- */
Gpio_InitStruct.u16PinOutputType = PIN_OUT_TYPE_CMOS; /* --正常CMOS电平输出或NMOS开漏输出--- */
Gpio_InitStruct.u16PinInputType = PIN_IN_TYPE_CMOS; /* --施密特输入或CMOS输入--- */
Gpio_InitStruct.u16PinState = PIN_STAT_SET; /* --初始化后电平状态--- */
Gpio_InitStruct.u16PullUp = PIN_PU_ON; /* --上拉输出--- */
Gpio_InitStruct.u16ExtInt = PIN_EXTINT_OFF; /* --中断功能不开启--- */
Gpio_InitStruct.u16Invert = PIN_INVT_OFF; /* --反向功能不开启--- */
Gpio_InitStruct.u16Latch = PIN_LATCH_OFF; /* --输出锁存功能不开启--- */
GPIO_Init( USART1_RX_PORT, USART1_RX_PIN, &Gpio_InitStruct );
/* Configure USART RX/TX pin. */
GPIO_SetFunc( USART1_RX_PORT, USART1_RX_PIN, GPIO_FUNC_33 );
GPIO_SetFunc( USART1_TX_PORT, USART1_TX_PIN, GPIO_FUNC_32 );
USART1_FCG_ENABLE(); /* ---使能串口1功能时钟--- */
USART_UART_StructInit( &Usart_InitStruct );
Usart_InitStruct.u32ClockSrc = USART_CLK_SRC_INTERNCLK; /* ---选择内部时钟--- */
Usart_InitStruct.u32ClockDiv = USART_CLK_DIV16; /* ---时钟64分频--- */
Usart_InitStruct.u32CKOutput = USART_CK_OUTPUT_DISABLE; /* ---不输出时钟--- */
Usart_InitStruct.u32Baudrate = 115200UL; /* ---波特率过高程序卡死时 请调整时钟分频因子--- */
Usart_InitStruct.u32Parity = USART_PARITY_NONE;
Usart_InitStruct.u32DataWidth = USART_DATA_WIDTH_8BIT;
Usart_InitStruct.u32StopBit = USART_STOPBIT_1BIT;
Usart_InitStruct.u32FirstBit = USART_FIRST_BIT_LSB; /* ---LSB先行--- */
Usart_InitStruct.u32HWFlowControl = USART_HW_FLOWCTRL_NONE;
Usart_InitStruct.u32StartBitPolarity = USART_START_BIT_LOW; /* ---起始位为低--- */
Usart_InitStruct.u32OverSampleBit = USART_OVER_SAMPLE_16BIT; /* ---16倍过采样--- */
if ( LL_OK != USART_UART_Init( CM_USART1, &Usart_InitStruct, NULL ) )
{
for ( ;; )
{
}
}
stcIrqSigninConfig.enIRQn = INT137_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_RI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_RxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 0UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
stcIrqSigninConfig.enIRQn = INT001_IRQn; /* ---中断向量号--- */
stcIrqSigninConfig.enIntSrc = INT_SRC_USART1_TI; /* ---中断类型--- */
stcIrqSigninConfig.pfnCallback = &USART1_TxCpltCallback; /* ---中断回调函数--- */
INTC_IrqSignIn( &stcIrqSigninConfig );
NVIC_ClearPendingIRQ( stcIrqSigninConfig.enIRQn ); /* ---清中断--- */
NVIC_SetPriority( stcIrqSigninConfig.enIRQn, 0UL ); /* ---设置中断优先级--- */
NVIC_EnableIRQ( stcIrqSigninConfig.enIRQn );
LL_PERIPH_WP( LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_INTC ); /* ---上保护--- */
USART_FuncCmd( CM_USART1, ( USART_TX | USART_RX | USART_INT_RX ), ENABLE ); /* ---打开发送接收功能、中断接收功能--- */
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
COM1_Info.pRxBuf = RxBuf;
COM1_Info.pTxBuf = TxBuf;
}
以下是串口中断接收回调函数代码:
/**************************************************************
* @Name USART1_RxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-20
**************************************************************/
static void USART1_RxCpltCallback( void )
{
uint8_t u8Data = 0x0;
if( USART_GetStatus( CM_USART1, USART_FLAG_RX_FULL ) != RESET )
{
u8Data = ( uint8_t )USART_ReadData( CM_USART1 );
COM1_Info.pRxBuf[COM1_Info.RxCnt] = u8Data;
USART_WriteData( CM_USART1, COM1_Info.pRxBuf[COM1_Info.RxCnt] );
COM1_Info.RxCnt++;
}
}
以下是串口中断发送回调函数代码:
/**************************************************************
* @Name USART1_TxCpltCallback
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-21
**************************************************************/
static void USART1_TxCpltCallback( void )
{
if( USART_GetStatus( CM_USART1, USART_FLAG_TX_EMPTY ) != RESET )
{
if( COM1_Info.TxLen > 0x00 )
{
CM_USART1->DR = COM1_Info.pTxBuf[COM1_Info.TxCnt] & 0X01FF;
COM1_Info.TxCnt++;
COM1_Info.TxLen--;
}
else
{
COM1_Info.TxLen = 0x00;
COM1_Info.TxCnt = 0x00;
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, DISABLE );
}
}
}
/**************************************************************
* @Name USART1_SendData_UseIT
* @brief
* @param pBuf: [输入/出]
** pLen: [输入/出]
* @retval
* @author Zachary
* @Data 2023-02-23
**************************************************************/
void USART1_SendData_UseIT( uint8_t *pBuf, uint8_t pLen )
{
COM1_Info.TxCnt = 0x00;
COM1_Info.TxLen = pLen;
memcpy( COM1_Info.pTxBuf, pBuf, COM1_Info.TxLen );
CM_USART1->DR = pBuf[COM1_Info.TxCnt];
COM1_Info.TxCnt++;
COM1_Info.TxLen--;
USART_FuncCmd( CM_USART1, USART_INT_TX_EMPTY, ENABLE );
}
以下是主程序代码:
/**************************************************************
* @Name main
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2022-02-13
**************************************************************/
int32_t main( void )
{
char SendBuf[] = "Hello World\n";
SysInit();
printf("This is HC32F4A0 USART1 test.\n");
USART1_SendData_UseIT( ( uint8_t * )SendBuf, strlen( SendBuf ) );
while( 1 )
{
LED10_TOGGLE();
LED11_TOGGLE();
delay_ms( 500 );
// printf("This is HC32F4A0 USART1 test.\n");
// USART1_SendData_UseIT( ( uint8_t * )SendBuf, strlen( SendBuf ) );
}
}
/**************************************************************
* @Name SysInit
* @brief
* @param None
* @retval
* @author Zachary
* @Data 2023-02-13
**************************************************************/
static void SysInit( void )
{
NVIC_SetPriorityGrouping( 0x00000003U ); /* ---设置为4位抢占优先级 */
bsp_SetSysClk();
CLK_GetClockFreq( &sysclk ); /* ---检查时钟是否正确--- */
delay_init( 240 ); /* ---系统滴答定时器初始化--- */
// delay_ms( 1000 );
bsp_Gpio_Init(); /* ---初始化Gpio--- */
bsp_Usart_Init();
}
以下是串口查询发送与中断发送测试图片,This is HC32F4A0 USART1 test.用的是查询发送,hello world用的是中断发送。
以下是串口中断回显测试图片: