聆思文档中心

SDK 使用说明

SDK 使用说明

1. A8 软件平台简介

基本框架

A8 Product 从上往下分别是:

  • APP 层 - 应用程序层

  • App Framework 层 - 应用程序框架层

  • RF Stack 层 - 射频协议栈层

  • Platform 层 - 平台层

  • HAL 层 - 硬件抽象层

其中 RF Stack 层、Platform 层、HAL 层已做封装,用户主要在 APP 层App Framework 层 进行二次开发。

---

应用程序框架

用户可以使用 SDK 提供的应用程序管理框架 APP Manager,进行上层应用功能开发。该框架可以使开发者尽可能少的关注对讲机应用各个模块的交互,比如休眠后的对讲机按键不生效、切换信道时通话业务必须结束等等交互可以自动完成,开发者可以更多的专注于各自功能的实现。

平台相关接口

主要是操作系统相关接口、存储相关接口和 DMR 协议栈相关接口。

硬件配置

用户可以根据自己的 PCB 特性,在应用层配置如何应用 GPIO、UART、SPI 等片上资源。

---

层级名称说明
APP 层用户二次开发区域 / 应用程序层 / 应用框架
App Framework 层应用框架支撑层
RF Stack 层射频通信协议栈(已封装)
Platform 层平台服务层(已封装)
HAL 层硬件抽象层(已封装)
硬件层底层物理硬件 (GPIO, UART, SPI...)

---

开放的代码

  • app: 功能和用户界面响应

  • 1 信道扫描,切换

  • 2 上位机通信

  • 3 录音

  • 4 GPS发送

  • 5 蓝牙管理

  • 6 电池管理

  • 7 漫游: 选择信号强的中继台

  • 8 GpsTx: 发送GPS到调度台

  • 9 GpsQuery: 查询信息,显示经纬度

  • 10 亚音码设置

  • Framework: 操作系统,嵌入式UI

  • rtos

  • ui system

---

SDK封装的代码

  • Core

  • bcpu:基带芯片相关,射频收发,音频协处理器算法等

  • xcpu:系统芯片相关的设备,总线等

  • platform

  • base / calib / chip / csw

  • edrv:总线驱动 atvd/aud/i2c/mcd(sdmmc)/memd(flash)/pmd/rfd/spi/tsd/voc

  • stack / svc

  • shared

  • hal:硬件抽象层: i2C/uart driver

  • os:嵌入式操作系统

  • target:目标板资源配置 tgtappcfg.h/tgtboardcfg.h

  • rfStack:协议层,如ADR, PDT, DMR, DCR

  • bcpu:协议的数据链路层,物理层

  • analog:模拟信号的收发

  • bcpu_analog: NBFM窄带调制与解调,发送与接送控制

  • cdcss/ctcss: 数字/模拟亚音

  • digital:数字信号的收发

  • bcpudigxcvr.c / bcpudigPhy.c / bcpudigRxSleep.c / dcrreceiver.c

  • xcpu:CCL层即呼叫控制层

  • algo / ana / audio

  • mdr/pdt/dcr: 数字业务,GPS信息解析

---

APP – 应用程序

  • appFramework – 应用接口层(对Core和RF Stack库封装为app提供访问接口, 还包含GUI, 输入法)

  • Core – 芯片初始化,启动引导, rtos运行, 文件系统实现, RF模块驱动, 芯片外设驱动, 基带信号处理

  • rfStack – 射频无线通讯协议层,模拟、PDT数字、DMR数字通讯协议栈, xcpu还包括语音,speex,nvoc和tts模块

---

2. 前置说明

2.1 APP 层

消息处理机制

不同来源的消息在不同的 MsgFilter.c 文件中配置。

AppFrame 包含了各交互系统,外部发送给 APP 的消息可分为下列类型:

C
typedef enum {
  KEY_INPUT   = 8,
  KEYPAD_INPUT,
  GUI_INPUT,
  APP_INPUT,
  PS_INPUT,
  LL_INPUT,
  TIMER_INPUT,
  /* ... 更多消息类型 */
} MSG_TYPE_E;

每种来源的消息有专门的"过滤文件"负责接收和分发。

不同来源的消息在不同的 MsgFilter.c 文件中配置

项目中有这些文件:

  • AF_KeyMsgFilter.c — 处理 KEY_INPUT(按键、旋钮)

  • AF_PSMsgFilter.c — 处理 PS_INPUT(协议栈事件)

  • AF_GuiMsgFilter.c — 处理 GUI_INPUT(界面操作)

  • AF_LLMsgFilter.c — 处理 LL_INPUT(链路层)

  • AF_AppMsgFilter.c — 处理 APP_INPUT(应用内部)

  • AF_TimerMsgFilter.c — 处理 TIMER_INPUT(定时器)

每个文件只处理一种来源的消息。

---

AppFrame 包含了各交互系统

AppFrame 是总调度器,根据 messageOrigin_t 把消息路由到正确的 MsgFilter:

C
switch(source)
{
  case KEY_INPUT:
    pAppManager->MsgFilter = AF_KeyMsgFilter; // 按键消息走 AF_KeyMsgFilter.c
    break;
  case PS_INPUT:
    pAppManager->MsgFilter = AF_PSMsgFilter;  // 射频消息走 AF_PSMsgFilter.c
    break;
  // ... 其他来源
}

---

消息类型列表

C
typedef enum {
  KEY_INPUT = 8,  // 按键/旋钮 → AF_KeyMsgFilter
  KEYPAD_INPUT,   // 键盘
  GUI_INPUT,    // 界面操作 → AF_GuiMsgFilter
  APP_INPUT,    // 应用内部
  PS_INPUT,     // 射频协议栈 → AF_PSMsgFilter
  LL_INPUT,     // 链路层
  TIMER_INPUT,   // 定时器
} messageOrigin_t;

---

实现堆栈事件的回调函数,处理消息的接收和发送

"堆栈" = 射频协议栈(Protocol Stack)。协议栈有事件(收到语音、收到数据)时,会调用回调函数通知 AppFrame,AppFrame 再发消息给代码:

JavaScript
协议栈收到语音 → 调用回调 → PS_INPUT → AF_PSMsgFilter → App_ChannelChange 处理
协议栈收到数据 → 调用回调 → PS_INPUT → AF_PSMsgFilter → App_DataRx 处理

简单总结:

JavaScript
消息来源 → messageOrigin_t 标签 → MsgFilter 文件 → App_XXX.c 处
KEY_INPUT: 从按键任务传入的消息
KEYPAD_INPUT: 从按键任务传入的消息,用于有keypad键的机器
GUI_INPUT: 从GUI传入的消息
APP_INPUT: 从另一个APP通过void AF_SendMsgToApp ()函数传过来的消息
PS_INPUT: 从协议栈传上来的消息
LL_INPUT: 从应用层其他地方传来的消息
TIMER_INPUT: APP中定义的定时器超时后返回的消息

APP_PRIORITY_KEYLOCK优先级最高

2.2 函数入口

  • APP交互表(AF_Interaction.h)

保存各个APP的状态 (激活, 关闭等) const InteractionTypet InteractionTable[APPMAX ][APPMAX] = {…}

  • 语音发送/接收流程

发送: 按键任务keyTaskEntry()中执行usrpttProcessHandle()检测PPT, 将PPT消息发送到AppDigitalVoice()进行发射处理 接收:协议栈接受到语音后将消息发送到App_DigitalVoice()进行接收处理

  • 数据发送/接收流程

AppDataTx() : 短信,补充业务,GPS发送 AppDataRx()或App_Alarm() : 数据接收

JavaScript
void * mmiTaskEntry(void *arg); 用于APP和GUI之间的数据传输
void *ShowAppAnimationEntry(void *arg); 用于显示开机动画
void *usrVocRecEntry(void *arg); 录音任务
void * keyTaskEntry(void *arg); 按键检测任务
void * knobTaskEntry(void *arg); 信道旋钮任务
void *appManageSleepTask(void *arg); 休眠管理任务
void * usrMainTaskEntry(void *arg); 应用层主任务
void * ATGUI_InstTaskEntry(void *arg); GUI任务

masterTaskEntry

项目说明
函数名static void masterTaskEntry(void *pData)
函数说明这是封装后的入口函数,可将此理解为类似于 main() 主函数。将初始化我们整个系统的相关任务和数据结构。同时又是一个任务体,在程序运行中处理接收到的按键面板的按键属性事件和省电醒来事件。

项目中对应的实际代码:

C
// 项目入口在 usr_main.c 中

void
productInit(
	void **gash,
	HANDLE htask
	)
{
  // 1. 初始化 RTOS
  RTOS_Init();
  
  // 2. 创建系统实例
  struct usrInstance_t *inst = zmalloc(sizeof(struct usrInstance_t));
  
  // 3. 创建所有任务
  createTask(&inst->keyTask, "keytask", 0, keyTaskEntry, inst);
  createTask(&inst->knobTask, "knobtask", 0, knobTaskEntry, inst);
  createTask(&inst->mmiTask, "mmitask", 0, mmiTaskEntry, inst);
  createTask(&inst->usrMainTask, "maintask", 0, usrMainTaskEntry, inst);
  
  // 4. 初始化硬件
  usr_KnodInitGpio();
  usr_KeyInit();
  
  return 0;
}

任务体

C
void * usrMainTaskEntry(void *arg) 
{
  while(1)
  {
    // 等待消息(按键、旋钮、射频事件等)
    os->pendMessage(inst->usrMainTask, &ev, 1);
    
    // 处理事件
    usr_MainTaskEvHandle(inst, ev);
    
    // 释放消息内存
    os->free(ev);
  }
}

处理按键面板事件

C
// 在 usrMainTaskEntry 中:
case KEY_INPUT:
  usr_programKeyDetection(inst); // 处理按键
  break;

处理省电醒来事件

C
// 从睡眠唤醒时,RTOS 会唤醒 usrMainTask:
case PS_INPUT:
  // 处理唤醒事件
  break;

---

简单总结:

JavaScript
void productInit(void **gash, HANDLE htask) → 创建所有任务 → 任务开始运行
  ↓
usrMainTaskEntry → 一直循环等待消息 → 收到消息就处理
  ↓
"既是入口函数又是任务体"

2.3 用户实例结构定义

该结构定义了我们整个系统的相关应用和数据,比如 OS、各个系统任务句柄、应用数据等。

C
struct usrInstance_t
{
	/* RTOS object */
	RTOS_t				*os;
	/* Task/Thread */
	void        *cmdTask;
	void				*mmiTask;
#ifdef A6_WITH_ATGUI	
	void				*guiTask;
	void				*appAnimationTask;
#endif
	void				*keyTask;
	void				*knobTask;
	void        *atecpsTask;
	void        *hostPortTask;
	void        *activeHostTask;
	void        *appSleepTask;
	void        *usrMainTask;
	void				*usrVrTask;
#ifdef UCDC_SERVICE
	void 				*ucdcTask;
  void        *ucdcTxBuf; /* buffer to hold received data to be sent back */
  unsigned short    ucdcTxLen;
#endif

	void        *lcdBkLightTimer;
	void        *keypadLightTimer;
	void        *volumeDispTimer;
	void        *cpsEnabelTimer;
	void        *battLowPowerTimer;
	void 				*dtmfRecvTimer;
	void 				*dtmfResetTimer;
	void 				*powerOnTimer;
	void         *motorTimer;
	void        *sleepLedTimer;
	void        *keyTaskCheckTimer;
	void        *bluetoothSleepTimer;
	void        *recDataReadTimer;
	bool        keypadLockStatus;
	bool        channelLockStatus;	
	void				*nvRamSaveAllDelayTimer;

	
	/* RF transceiver instance */
	//const struct RFIC_t *rfic;

	/* dig/ana profile */
	DIG_CHAN_PROFILE_t *digChnProfile;
	ANA_CHAN_PROFILE_t *anaChnProfile;

	xstackInstance_t	*stackInst;
	char        scanStatus;
	BOOL				isMmiStart;
	BOOL				isKeyTaskStart;
	unsigned short		knodChannel;
	unsigned short		zoneIndex;
	unsigned char		appNvramDataStatus; //1-valid,0-invalid,2-need to be save
	BOOL				voxStartFlag;
	BOOL				isKnodTaskActive;
	BOOL        earPhoneDetEnable;
	HSEARPHONE_TYPE_t  earphoneType;
	BOOL        earphoneDetected;
	BOOL        earphoneKeyStatus;
	BOOL        earPhonePttAdcDet;	//EXT PTT detection mode : 1- ADC , 0-GPIO
  unsigned char    eqEnable;
  short        eqGain[3];
	unsigned char 		vBatPercent;	// battery percent value(0~100).
	unsigned char		lcdBkLightCtrl; // 0-back light always open,>0-control by timer(seconds)
	unsigned char		isLcdBkLightOpen; // 1-open,0-close	
  unsigned char    volume;
  unsigned char    lcdBrightness;   // 1~7
  unsigned char    pushToTalkIdx;		// key 0~4

	BOOL				isAppStart;
	BOOL        isTalkAround;
	BOOL        isSimpleEncrypt;  //1-enable , 0 - disable
  BOOL        isLedDispClose;   //1-off display function , 0 - enable
  BOOL        isSaveAppData;   //1-need to be save
  BOOL        isSaveCpsData;
  BOOL        isBattLowPower;   // 1-low power 0 - normal
  BOOL        isLowPowerBanTx;  // 1-ban Tx 0 - normal
  BOOL				isBattAdcAdjust;	// 1-into 0 - exit 
  BOOL        isStackDisable;    
	BOOL        isLastPwrKeyState; 
	BOOL				isNullChannel;
	BOOL        isEmerg;      //1-emerg start 0-emerg stop
	BOOL        isEmergAlert; 
	BOOL        isCallAlert;
	BOOL        isTxAlarmState;
	BOOL				isPttIdOnceMode;
	BOOL				isDtmfSendEn;
	BOOL				isQciiSending;
	BOOL				isCpsRunn;			//1-running
	BOOL				isDigSync;			//1-sync 0-async
	BOOL				isPowerOn;			//1-power on 
	BOOL        isKeyDetectEn;
	BOOL				isPowerDown;
	BOOL        isEmergStay;  // 1:Emergency stay state
	BOOL				isDsmActive;
	BOOL        isRoamingStay;
	BOOL        isCallInterrupted; 
	DebugSetting_t   debugSetting;
	BOOL        isBlueToothConnect;
	BOOL        isExitScan;
	BOOL        isLowTemperature;
	BOOL				knobSwitchDir;
	BOOL        isAprsSendFlag;
	BOOL        isAprsDispFlag;
	short  		  testVoxTh;
	BOOL				isRecordEn;
	BOOL        isZoneChangeFlag;  
	char        isKeyDown;
	BOOL        isRecordFileReadingState; 
	int					isCsvWR;
}; // ← 这里就是结尾



// 外部函数声明
extern struct usrInstance_t * usr_GetInst(void);

#endif // _USR_PRIVATE_H_

---

如何在代码中使用这个结构体?

因为没有使用 typedef,所以在所有使用这个结构体的地方都必须加上 struct 关键字:

使用方式:

C
// ? 正确:使用 struct 关键字
struct usrInstance_t *inst = usr_GetInst();
struct usrInstance_t *inst2;

// ? 错误:不能直接使用类型名
// usrInstance_t *inst; // 编译错误!


#### 在项目中的实际使用:


// usr_key.c
void usr_OnMsgHwKey(struct usrInstance_t *inst, KEY_EVENT_T *ev)
{
  // 直接使用 struct usrInstance_t 作为类型
  inst->knodChannel = USR_DEFAULT_CH;
}

// usr_maintask.c
void * usrMainTaskEntry(void *arg)
{
  struct usrInstance_t *inst = (struct usrInstance_t *)arg;
  // 使用 inst->xxx 访问成员
  inst->isAppStart = TRUE;
}

// usr_gui.c
void ledgui_channelInfo(unsigned int channel, unsigned short position)
{
  struct usrInstance_t *inst = usr_GetInst();
  // 使用 inst 指针
  ledDisplay_battLevel(usr_BattPerc2Level(inst->vBatPercent), LED_ON);
}

---

3. OS基本介绍

SDK 封装了 RTOS 相关接口,提供以下功能:

接口类别主要功能
任务管理创建、删除、挂起、恢复任务
信号量创建、获取、释放信号量
消息队列发送、接收消息
定时器创建、启动、停止定时器
内存管理动态内存分配/释放

---

RTOS OS 接口汇总

所有 OS 接口都通过 inst->os 调用,定义在 rtos.h 中。

3.1 任务管理

C
int (*createTask)(void **taskHandle, char *name, int priority, TASK_ENTRY(task), void *arg);
// 示例: inst->os->createTask(&inst->keyTask, "keytask", 0, keyTaskEntry, (void *)inst);

int (*sleepTask)(int duration);
// 示例: inst->os->sleepTask(10); // 延时 10ms

int (*pauseTask)(void *taskHandle);
// 示例: inst->os->pauseTask(inst->keyTask); // 挂起任务

int (*wakeupTask)(void *taskHandle);
// 示例: inst->os->wakeupTask(inst->keyTask); // 唤醒任务

3.2 消息队列

C
int (*postMessage)(void *taskHandle, void *msg);
// 示例: inst->os->postMessage(inst->knobTask, (void *)msg);

int (*pendMessage)(void *taskHandle, void **msg, int timeout);
// 示例: inst->os->pendMessage(inst->mmiTask, &msg, 1);

3.3 定时器

C
int (*createTimer)(void **timerHandle, void (*callback)(void *), void *arg, int period);
// 示例: inst->os->createTimer(&timer, keyTaskCheckCb, inst, 50);

int (*startTimer)(void *timerHandle);
// 示例: inst->os->startTimer(inst->keyTaskCheckTimer);

int (*stopTimer)(void *timerHandle);
// 示例: inst->os->stopTimer(inst->keyTaskCheckTimer);

3.4 内存管理

C
void *(*zmalloc)(size_t size); // 分配已清零的内存
// 示例: USR_MSG_t *msg = os->zmalloc(sizeof(USR_MSG_t));

void *(*malloc)(size_t size);  // 分配未初始化的内存

void (*free)(void *ptr);     // 释放内存
// 示例: os->free(msg);

---

4 GPIO 接口

GPIO(General Purpose Input/Output)接口用于控制通用输入输出引脚。

常用接口:

4.1 GPIO 接口(hal_gpio.h)

功能函数
配置输入hal_GpioSetIn(pin)
配置输出hal_GpioSetOut(pin)
读取电平hal_GpioGet(pin)
输出高hal_GpioSet(pin)
输出低hal_GpioClr(pin)
清除中断hal_GpioResetIrq(pin)
设置中断触发hal_GpioIrqSetMask(pin, &cfg)
注册中断回调hal_GpioIrqSetHandler(pin, handler)

5 UART 接口(hal_uart.h)

功能函数调用参数与说明
配置并打开串口uartConfigure(port, baudRate, readUartTask)port: 串口编号(HAL_UART_1~4);baudRate: 波特率;readUartTask: 接收数据任务句柄。用于初始化串口参数并激活通道。
串口接收数据uartRead(port, pDataBuf, length)port: 串口编号;pDataBuf: 接收缓冲区指针;length: 期望读取字节数。成功返回实际读取字节数,失败返回 0
串口发送数据uartWrite(port, pDataBuf, length)port: 串口编号;pDataBuf: 发送缓冲区指针;length: 待发送字节数。成功返回实际写入字节数,失败返回 0
C
(1)	驱动配置
pdt_board_cfg.h
…
#undef TGT_HAL_UART_CFG
#define TGT_HAL_UART_CFG \
{                       \
  HAL_UART_CONFIG_DATA, /* UART1配置 */ \
HAL_UART_CONFIG_DATA /* UART2配置*/  \
 	HAL_UART_CONFIG_NONE,                        \
 	HAL_UART_CONFIG_NONE,                        \
}
配置说明:
HAL_UART_CONFIG_NONE:不使用该串口
HAL_UART_CONFIG_DATA:使能该串口,但仅使用TXD/RXD管脚。
HAL_UART_CONFIG_FLOWCONTROL: 使能该串口,带流控,包括RTS和CTS。
注意:
当管脚配置为串口功能后,四个PIN均需要同时作为UART使用,不能配置为GPIO。

(2)	串口配置
函数原型  	
void uartConfigure(
	HAL_UART_ID_T port,
	int BaudRate,
	void *readUartTask
	);
表头文件  
uc_uart.h
函数说明 
配置串口参数,并打开该串口。
port:HAL_UART_1,HAL_UART_2,HAL_UART_3,HAL_UART_4
baudrate: 波特率
readUartTask: 串口任务句柄
返回值 
无
附加说明 
范例 
#include “uc_uart.h”
……
unsigned char rx_buf[4] = {0};
unsigned char tx_buf[4] = {0, 1, 2, 3};
int ret;

//打开串口1,设置波特率9600,
uartConfigure(HAL_UART_1,9600, readUartTask); 

ret = uartRead(HAL_UART_1, rx_buf, sizeof(rx_buf)); //读取串口1数据
if(ret != 0) printf(“get %d bytes from uart”, ret);

ret = uartWrite(HAL_UART_1, tx_buf, sizeof(tx_buf)); //串口1发送数据
if(ret != 0) printf(“send %d bytes to uart”, ret);

(3)	串口读数据
函数原型  	
int uartRead(
	HAL_UART_ID_T port,
  void *pDataBuf,
  int length
);
表头文件  
uc_uart.h
函数说明 
串口读数据。
port:HAL_UART_1,HAL_UART_2,HAL_UART_3,HAL_UART_4
rxBuf:读取数据的缓冲区
rxLen: 缓冲区长度。
返回值 
成功返回实际读取到的字节数,失败返回0。
附加说明 
范例
 参考上述“串口配置”接口。

(4)	串口写数据
函数原型  	
int uartWrite(
	HAL_UART_ID_T port,
  void *pDataBuf,
  int length
);
表头文件  
uc_uart.h
函数说明 
串口写数据。
port:HAL_UART_1,HAL_UART_2,HAL_UART_3,HAL_UART_4
rxBuf:写数据的缓冲区
rxLen: 缓冲区长度。
返回值 
成功返回实际写入的字节数,失败返回0。
附加说明 
范例 
参考上述“串口配置”接口。

6 SPI 接口(hal_spi.h)

功能函数调用参数与说明
打开并配置 SPI 接口hal_SpiOpen(id, csNum, &cfg)初始化 SPI 控制器,配置片选、时钟极性/相位、速率、FIFO 触发阈值、中断掩码及轮询/中断模式
激活片选(启动通信)hal_SpiActivateCs(id, csNum)拉低指定片选信号,准备开始数据传输;成功返回 TRUE
发送数据hal_SpiSendData(id, csNum, txBuf, len)将数据写入 SPI 发送 FIFO,硬件自动移位发送(原文示例写为 halSpiSendData
等待发送完成hal_SpiTxFinished(id, csNum)轮询等待 TX FIFO 为空/移位寄存器空闲,确保数据全部发出
接收数据hal_SpiGetData(id, csNum, rxBuf, len)从 SPI 接收 FIFO 读取指定长度数据(原文示例写为 FRMWK_halSpiGetData
释放片选(结束通信)hal_SpiDeActivateCs(id, csNum)拉高片选信号,结束本次 SPI 通信周期
关闭 SPI 接口hal_SpiClose(id, csNum)关闭指定 SPI 控制器,恢复引脚为 GPIO 默认状态,释放资源

(1) 驱动配置

C
pdt_board_cfg.h
…
#undef TGT_HAL_GPIO_12_USED
#define TGT_HAL_GPIO_12_USED	   AS_ALT_FUNC //SPI1_CLK
#undef TGT_HAL_GPIO_14_USED
#define TGT_HAL_GPIO_14_USED	   AS_ALT_FUNC //SPI1_CS_1
#undef TGT_HAL_GPIO_15_USED
#define TGT_HAL_GPIO_15_USED	   AS_ALT_FUNC //SPI1_DIO
#undef TGT_HAL_GPIO_16_USED
#define TGT_HAL_GPIO_16_USED	   AS_ALT_FUNC //SPI1_DI

上述配置将GPIO管脚配置为SPI1功能管脚,SPI1支持两个片选: SPI1_CS_1(GPIO_14)、SPI1_CS_0(GPIO_6),即可支持连接两个SPI Device,但同一时刻只能访问一个SPI Device。
…
#undef TGT_HAL_SPI_CFG
#define TGT_HAL_SPI_CFG                        \
{                                  \
  {/* SPI1 */                             \
    .cs0Used    = FALSE,                    \
    .cs1Used    = TRUE,                   \
    .cs2Used    = FALSE,                    \
    .cs3Used    = FALSE,                    \
    .cs0ActiveLow  = FALSE,                    \
    .cs1ActiveLow  = TRUE,                    \
    .cs2ActiveLow  = FALSE,                    \
    .cs3ActiveLow  = FALSE,                    \
    .di0Used    = TRUE,                    \
    .di1Used    = TRUE                     \
  },                                 \
  {/* SPI2 */                             \
    .cs0Used    = FALSE,                    \
    .cs1Used    = FALSE,                    \
    .cs2Used    = FALSE,                    \
    .cs3Used    = FALSE,                    \
    .cs0ActiveLow  = FALSE,                    \
    .cs1ActiveLow  = FALSE,                    \
    .cs2ActiveLow  = FALSE,                    \
    .cs3ActiveLow  = FALSE,                    \
    .di0Used    = FALSE,                    \
    .di1Used    = FALSE                     \
  }                                \
}
该配置说明:
.cs1Used :使能/禁止SPI片选1
.cs1ActiveLow:SPI片选是否低电平有效
.di0Used:是否接收CS0 SPI Device数据
.di1Used:是否接收CS1 SPI Device数据

(2)	打开SPI
函数原型  	
PUBLIC VOID hal_SpiOpen(HAL_SPI_ID_T id, HAL_SPI_CS_T csNum, CONST HAL_SPI_CFG_T* spiCfg);
表头文件  
hal_spi.h
函数说明 
配置SPI参数,并打开SPI口。
id: SPI编号
typedef enum
{
  HAL_SPI,
  HAL_SPI_2,
#if (CHIP_SPI2_CS_QTY == 0)
  HAL_SPI_QTY = 1
#else
  HAL_SPI_QTY = 2
#endif
} HAL_SPI_ID_T;

csNum: 片选编号
typedef enum
{
  /// Chip Select 0
  HAL_SPI_CS0 = 0,
  /// Chip Select 1
  HAL_SPI_CS1,
  /// Chip Select 2
  HAL_SPI_CS2,
  /// Chip Select 3 - It doesn't necessarily exists for a given SPI.
  HAL_SPI_CS3,

  HAL_SPI_CS_QTY
 } HAL_SPI_CS_T;

spiCfg :SPI配置信息
typedef struct
{
  /// Select the Chip Select
  HAL_SPI_CS_T enabledCS; 

  /// Polarity of this CS
  BOOL csActiveLow;

  /// When \c TRUE, the emission commands will fill the Rx FIFO with read
  /// data, thus enabling the ability to receive data. \n
  /// When \c FALSE, nothing is written in the Rx FIFO when data are sent.
  /// It is not possible to read received data, which are discarded.
  BOOL inputEn;

  /// If the first edge after the CS activation is a falling edge, set to 
  /// \c TRUE.\n Otherwise, set to \c FALSE.
  BOOL clkFallEdge;

  /// The delay between the CS activation and the first clock edge,
  /// can be 0 to 2 half clocks.
  HAL_SPI_DELAY_T clkDelay; 

  /// The delay between the CS activation and the output of the data, 
  /// can be 0 to 2 half clocks.
  HAL_SPI_DELAY_T doDelay; 

  /// The delay between the CS activation and the sampling of the input data,
  /// can be 0 to 3 half clocks.
  HAL_SPI_DELAY_T diDelay; 

  /// The delay between the end of transfer and the CS deactivation, can be 
  /// 0 to 3 half clocks.
  HAL_SPI_DELAY_T csDelay;

  /// The time when the CS must remain deactivated before a new transfer, 
  /// can be 0 to 3 half clocks.
  HAL_SPI_DELAY_T csPulse; 

  /// Frame size in bits
  UINT32 frameSize;

  /// OE ratio - Value from 0 to 31 is the number of data out to transfert 
  /// before the SPI_DO pin switches to input. When 0m the SPI_DO pin switching 
  /// direction mode is not enabled.
  UINT8 oeRatio;
  
  /// SPI maximum clock frequency: the SPI clock will be the highest
  /// possible value inferior to this parameter.
  UINT32 spiFreq;

  /// Value for the reception FIFO above which an interrupt may be generated.
  HAL_SPI_RX_TRIGGER_CFG_T rxTrigger; 

  /// Value for the emission FIFO above which an interrupt may be generated.
  HAL_SPI_TX_TRIGGER_CFG_T txTrigger; 

  /// Reception transfer mode
  HAL_SPI_TRANSFERT_MODE_T rxMode;

  /// Emission transfer mode
  HAL_SPI_TRANSFERT_MODE_T txMode;

  /// IRQ mask for this CS
  HAL_SPI_IRQ_STATUS_T mask;

  /// IRQ handler for this CS;
  HAL_SPI_IRQ_HANDLER_T handler;

} HAL_SPI_CFG_T;
返回值 
无。
附加说明 
范例 
#include “hal_api.h”
…
HAL_SPI_CFG_T rwSpiCfg =  
{    
  .enabledCS  = HAL_SPI_CS1,
  .csActiveLow  = TRUE, // 片选低电平有效
  .inputEn    = TRUE, // 使能读数据
.clkFallEdge  = TRUE, // 片选信号打开后的第一个时钟是下降沿 
  .clkDelay    = 0,  // 片选信号打开与第一个时钟沿之间的延时
  .doDelay    = 0,  // 片选信号打开与数据输出之间的延时
  .diDelay    = 1,  // 片选信号打开与输入数据采样之间的延时
  .csDelay    = 1,  // 数据传输结束与片选信号关闭之间的延时
  .csPulse    = 0,  // 在新的数据传输前片选信号需保持无效的时间延时
  .frameSize   = 8, // 8位SPI 
  .oeRatio    = 8,
.spiFreq    = 1000000,// SPI时钟1MHz
.rxTrigger   = HAL_SPI_RX_TRIGGER_4_BYTE,
.txTrigger   = HAL_SPI_TX_TRIGGER_1_EMPTY,
.rxMode 		= HAL_SPI_DIRECT_POLLING, 
.txMode 		= HAL_SPI_DIRECT_POLLING,
.mask      = {0,0,0,0,0},
.handler    = NULL
};
unsigned char rx_buf[3] = {0};
unsigned char tx_buf[3] = {0xda, 0xff, 0xff};
int ret = 0;

HAL_SPI_ID_T g_lcdSpiId = HAL_SPI;
HAL_SPI_CS_T g_lcdSpiCs = HAL_SPI_CS1;
hal_SpiOpen(g_lcdSpiId, g_lcdSpiCs, &rwSpiCfg);
if(hal_SpiActivateCs(g_lcdSpiId, g_lcdSpiCs))
{
ret = halSpiSendData(g_lcdSpiId, g_lcdSpiCs,tx_buf, 3);
FRMWK_printf(“SPI send %d Bytes”, ret);
	if (ret == 3)
{
  while(!hal_SpiTxFinished(g_lcdSpiId, g_lcdSpiCs));
ret = FRMWK_halSpiGetData(g_lcdSpiId, g_lcdSpiCs,rx_buf, 3);
FRMWK_printf(“SPI get %d Bytes”, ret);
}
  hal_SpiDeActivateCs(g_lcdSpiId, g_lcdSpiCs);
else
{
  ret = -1;
}
halSpiClose(g_lcdSpiId, g_lcdSpiCs);
}

---

7 LCD 接口(lcdgui_*.h)

功能分类函数/宏/结构体参数与说明
硬件引脚配置TGT_SPILCD_CONFIG宏定义结构体:指定 LCD_A0 引脚、Reset 引脚、SPI 通道号及片选编号
驱动自动加载lcdd_LoadDrv()无参,返回 BOOL。根据 LCD 驱动 IC 型号自动匹配并注册底层控制函数
驱动函数注册lcdd_st7735r_RegInit(pLcdDrv)传入 LCDD_REG_T 结构体指针,将 LCD 控制函数集绑定到框架
电源控制lcdd_Open() / lcdd_Close()初始化 LCD 硬件 / 关闭 LCD 电源与背光
休眠与唤醒lcdd_Sleep() / lcdd_WakeUp()进入低功耗休眠状态 / 恢复正常显示状态
亮度/对比度lcdd_SetContrast(UINT32)设置屏幕背光强度或灰度对比度等级
待机模式lcdd_SetStandbyMode(BOOL)TRUE: 启用待机;FALSE: 禁用待机
局部刷新lcdd_PartialOn(vsa, vea) / lcdd_PartialOff()开启指定 Y 轴区域刷新 / 关闭局部刷新模式
像素绘制lcdd_SetPixel16(x, y, data)在坐标 (x,y) 写入 16-bit 色值像素点
矩形填充lcdd_FillRect16(region, color)在 ROI 区域填充指定背景色
块传输刷新lcdd_Blit16(fbWin, startX, startY)将帧缓冲区数据块快速拷贝至屏幕指定位置
屏幕信息查询lcdd_GetScreenInfo(info)获取屏幕分辨率、色深、扫描方向等基础参数
忙状态检测lcdd_Busy()返回 BOOL,查询 LCD 控制器是否正在处理数据
显示方向lcdd_SetDirRotation() / lcdd_SetDirDefault()切换屏幕显示方向 / 恢复默认横竖屏
GUI 框架初始化ATGUI_StartUp() + lcdgui_GuiSystemDataInit()初始化图形界面框架及系统全局 UI 数据
应用层 UI 函数lcdgui_channelInfo() / lcdgui_batteryChange()显示当前信道信息 / 动态更新电池电量图标
背光与全屏usr_InitBkLightCtrl() / usr_activeLcdBkLightCtrl() / lcdgui_fullScreen()初始化背光定时器 / 激活背光 / 开启或关闭全屏显示模式(原文 ledgui 应为拼写笔误)

目前支持串行SPI显示屏。 驱动源码文件夹对应:……\extDriver\lcdd\st7735r\slcd

C
(1)	LCD SPI 配置
表头文件 
pdt_board_cfg.h
…
#undef TGT_SPILCD_CONFIG
#define TGT_SPILCD_CONFIG                  \
{																\
	.lcdA0Pin  = { .gpoId = HAL_GPIO_4 }, 					  \
	.lcdResetPin = { .gpoId = HAL_GPIO_31 }, 					\
	.lcdSpiId = HAL_SPI, 											\
	.lcdSpiCs = HAL_SPI_CS1 									\
}

tgt_extdrive_config.c
…
PROTECTED CONST SPILCD_CONFIG_STRUCT_T g_tgtSpiLcdCfg = TGT_SPILCD_CONFIG;

(2)	加载LCD驱动
函数原型  	
PRIVATE BOOL lcdd_LoadDrv(VOID);
源文件  
lcdd.c
函数说明 
通过LCD ID加载正确的LCD驱动。
返回值 
成功返回1。

(3)	LCD功能配置函数体

typedef struct 
{ 
  LCDD_ERR_T (*lcdd_Open)(VOID );  // 打开
  LCDD_ERR_T (*lcdd_Close)(VOID);  // 关闭
  LCDD_ERR_T (*lcdd_SetContrast)(UINT32 contrast);
  LCDD_ERR_T (*lcdd_SetStandbyMode)(BOOL standbyMode);
  LCDD_ERR_T (*lcdd_Sleep)(VOID);
  LCDD_ERR_T (*lcdd_PartialOn)(UINT16 vsa, UINT16 vea);
  LCDD_ERR_T (*lcdd_PartialOff)(VOID);
  LCDD_ERR_T (*lcdd_WakeUp)(VOID);
  LCDD_ERR_T (*lcdd_GetScreenInfo)(LCDD_SCREEN_INFO_T* screenInfo);
  LCDD_ERR_T (*lcdd_SetPixel16)(UINT16 x, UINT16 y, UINT16 pixelData);
  LCDD_ERR_T (*lcdd_FillRect16)(CONST LCDD_ROI_T* regionOfInterrest, UINT16 bgColor);
  LCDD_ERR_T (*lcdd_Blit16)(CONST LCDD_FBW_T* frameBufferWin, UINT16 startX, UINT16 startY);
  BOOL (*lcdd_Busy)(VOID);
  BOOL (*lcdd_SetDirRotation)(VOID);
  BOOL (*lcdd_SetDirDefault)(VOID);
  char* (*lcdd_GetStringId)(VOID);
} LCDD_REG_T;

函数原型  	
BOOL lcdd_st7735r_RegInit(LCDD_REG_T *pLcdDrv);
头文件  
lcdd.h
函数说明 
配置LCD功能函数。
返回值 
成功返回1。
说明 
该函数已内部封装调用,用户修改对应的子功能配置函数即可。

---

8 增益接口/SPK 扬声器接口配置

---

场景参数类型宏定义模拟增益范围数字增益范围默认值
模拟信道 - 外放MICANA_MIC_ANA_GAIN / ANA_MIC_DIG_GAIN660~15

SPKANA_SPK_ANA_GAIN / ANA_SPK_DIG_GAIN15200~15 / 0~31
模拟信道 - 耳机MICANA_EARMIC_ANA_GAIN / ANA_EARMIC_DIG_GAIN660~15

SPKANA_EARSPK_ANA_GAIN / ANA_EARSPK_DIG_GAIN15200~15 / 0~31
数字信道 - 外放MICPDT_MIC_ANA_GAIN / PDT_MIC_DIG_GAIN640~15

SPKPDT_SPK_ANA_GAIN / PDT_SPK_DIG_GAIN15180~15 / 0~31
数字信道 - 耳机MICPDT_EARMIC_ANA_GAIN / PDT_EARMIC_DIG_GAIN640~15

SPKPDT_EARSPK_ANA_GAIN / PDT_EARSPK_DIG_GAIN15180~15 / 0~31
音频通道模式宏定义名称模拟增益值数字增益值有效范围
MIC 外放模拟信道ANA_MIC_ANA_GAIN / ANA_MIC_DIG_GAIN660~15

数字信道PDT_MIC_ANA_GAIN / PDT_MIC_DIG_GAIN640~15
MIC 耳机模拟信道ANA_EARMIC_ANA_GAIN / ANA_EARMIC_DIG_GAIN660~15

数字信道PDT_EARMIC_ANA_GAIN / PDT_EARMIC_DIG_GAIN640~15
SPK 外放模拟信道ANA_SPK_ANA_GAIN / ANA_SPK_DIG_GAIN15200~15 / 0~31

数字信道PDT_SPK_ANA_GAIN / PDT_SPK_DIG_GAIN15180~15 / 0~31
SPK 耳机模拟信道ANA_EARSPK_ANA_GAIN / ANA_EARSPK_DIG_GAIN15200~15 / 0~31

数字信道PDT_EARSPK_ANA_GAIN / PDT_EARSPK_DIG_GAIN15180~15 / 0~31

---

C
(1)	说明
注意,模拟数字模式下使用的是不同的MIC/SPK增益组合,并且带耳机和不带耳机使用的也是不同的MIC/SPK增益组合,使用时请注意区分。
表头文件
pdt_app_cfg.h
模拟信道,默认MIC增益,模拟增益值范围(0~15),数字增益值范围(0~15)
//模拟模式 
#define ANA_MIC_ANA_GAIN       (6)
#define ANA_MIC_DIG_GAIN       (6)
#define ANA_EARMIC_ANA_GAIN     (6)
#define ANA_EARMIC_DIG_GAIN     (6)
数字信道,默认MIC增益,模拟增益值范围(0~15),数字增益值范围(0~15)
//数字模式
#define PDT_MIC_ANA_GAIN       (6)
#define PDT_MIC_DIG_GAIN       (4)
#define PDT_EARMIC_ANA_GAIN     (6)
#define PDT_EARMIC_DIG_GAIN     (4)
模拟信道,默认SPK增益,模拟增益值范围(0~15),数字增益值范围(0~31)
//模拟模式
#define ANA_SPK_ANA_GAIN       (15)
#define ANA_SPK_DIG_GAIN       (20)
#define ANA_EARSPK_ANA_GAIN     (15)
#define ANA_EARSPK_DIG_GAIN     (20)
数字信道,默认SPK增益,模拟增益值范围(0~15),数字增益值范围(0~31)
//数字模式
#define PDT_SPK_ANA_GAIN       (15)
#define PDT_SPK_DIG_GAIN       (18)
#define PDT_EARSPK_ANA_GAIN     (15)
#define PDT_EARSPK_DIG_GAIN     (18)
(2)	初始化MIC/SPK增益
函数原型  	
void usr_setMicSpkGainDefault(void *arg)
表头文件  
Usr_maintask.h
函数说明 
设置MIC/SPK增益配置,注意,模拟数字模式下使用的是不同的MIC/SPK增益组合。
返回值 
无。

(3)	接收音量等级SPK配置表
文件
Usr_maintask.c

第一列表示模拟模式不带耳机,第二列数字模式不带耳机,第三列表示模拟模式带耳机,第四列数字模式带耳机
//p1:模拟信道的本地spk的数字增益
//p2:数字信道的本地spk的数字增益
//p3:模拟信道的耳机spk的数字增益
//p4:数字信道的耳机spk的数字增益
// 模拟增益(0~15) 
static unsigned char spkAnaGain_table[USR_VOLUME_MAX][4]=
{
  {0, 0, 0, 0},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12},
  {12, 12, 12, 12}
};
// 数字增益(0~31)
static unsigned char spkDigGain_table[USR_VOLUME_MAX][4] =
{ 
  {0, 0, 0, 0}, 
  {13, 12, 13, 12}, 
  {15, 14, 15, 14}, 
  {17, 16, 17, 16}, 
  {19, 18, 19, 18}, 
  {21, 20, 21, 20},
  {23, 22, 23, 22},
  {25, 24, 25, 24},
  {27, 26, 27, 26},
  {29, 28, 29, 28},
  {31, 30, 31, 30}
};

(4)	设置接收音量大小
函数原型  	
void usr_setVolume(void * arg, unsigned char vol);
表头文件  
Usr_maintask.h
函数说明 
Vol音量等级值。对应接收音量等级SPK增益表
返回值 
无。

---

9 Key 按键接口(usr_key.h)

TSD 按键处理函数与宏定义表

功能分类函数/宏/变量参数与说明
键值映射表g_tgtKeyMap[]CONST UINT8[] 全局常量数组,定义按键物理位置与逻辑键值的映射关系
键值映射宏KEY_MAPpdt_board_cfg.h 中定义,作为 g_tgtKeyMap 的初始化数据源
获取逻辑键值tgt_GetKeyCode(UINT8 key)传入物理按键索引,返回标准键码标识(文档标注成功返回 1
键值定义头文件key_defs.h包含平台标准键码定义(如 KP_1~KP_SR 等)

---

配置项功能说明 / 注意事项
penGpio只能为 HAL_GPIO_0
debounceTime表示消抖时间(ms),时间越长键值判断越准确,当然时间太长,按键判断会响应迟钝
downPeriod表示按键按下后经过时间(downPeriod * debounceTime)后再去读键值
upPeriod表示按键弹起后经过时间(upPeriod * debounceTime)后再去读键值
maxError读KEY_SENSE值如果大于该值表示出错
keyCount表示按键个数(最大值22)
minVolt表示KEY_SENSE读到的最小电压
maxVolt表示KEY_SENSE读到的最大电压
useVoltTbl请设置为 TRUE,这表示根据 tblHighVolttblLowVolt 的电压表来判断键值,即当 tblLowVolt[i] <= 按键按下的电压值 <= tblHighVolt[i] 时第 i 个按键被按下。
tblHighVolti 个按键按下电压最大值。
该值以实测为准,可根据驱动调试打印信息来确定。当按键按下时 coolWatcher Trace tool 会出现如下打印:

EDRV WARN : tsd_GetKeyIndex volt=51, index=0

volt 表示当前按下的按键电压值,index 表示被按下的按键索引。
tblLowVolti 个按键按下电压最小值。
该值以实测为准,可根据驱动调试打印信息来确定。当按键按下时 coolWatcher Trace tool 会出现如下打印:

EDRV WARN : tsd_GetKeyIndex volt=51, index=0

volt 表示当前按下的按键电压值,index 表示被按下的按键索引。

---

C
(1)	驱动配置
pdt_board_cfg.h
…
#undef TGT_TSD_CONFIG
#define TGT_TSD_CONFIG                       \
{                                  \
  .penGpio     = HAL_GPIO_0,                  \
  .debounceTime  = 16*HAL_TICK1S/1000,              \
  .downPeriod   = 10,                      \
  .upPeriod     = 3,                      \
  .maxError     = 980,                     \
  .keyCount    = 18,                      \
  .minVolt     = 69,                     \
  .maxVolt     = 953,                     \
  .useVoltTbl    = TRUE,                    \
  .tblHighVolt  = {143,231,302,357,412,440,472,494,510,531,551,581,611,671,732,811,872,979}, \
  .tblLowVolt  = {49 ,144,232,303,358,413,441,473,495,511,532,552,582,612,672,733,812,873}, \
}
a)	配置说明:
TSD keypad其实质是利用触摸屏在KEYP和KEYN两端输出一个电压,A6外围在这两端串联N个阻,电阻之间接上N个按键,所有按键另一端接在一起并接到A6的KEY_SENSE引脚上,A6利用按键按下时,通过内部GPIO_0产生一个中断,并在中断触发读KEY_SENSE的电压来判断键值。

b)	键值定义
A6平台标准的键值定义在头文件key_defs.h,当按键被按下时,驱动层会根据KEY_MAP(该宏在pdt_board_cfg.h)的定义给应用层APP上报按键消息,下截图被选中的部分(KP_1、KP_2…KP_SR)对应上述TGT_TSD_CONFIG 定义的18个按键(0~18)。
C
#define KEY_MAP                              											\
{			/*	KEYIN 0 	KEYIN 1 	KEYIN 2 	KEYIN 3 	KEYIN 4 	KEYIN 5 	KEYIN 6 	KEYIN 7 */		\
/*KYEOUT 0 */	KP_UNMAPPED,KP_UNMAPPED,KP_STAR,	KP_0,		KP_BACK,	KP_POUND,	KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 1 */	KP_UNMAPPED,KP_UNMAPPED,KP_3,		KP_6,		KP_DW,		KP_9,		KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 2 */	KP_UNMAPPED,KP_UNMAPPED,KP_2,		KP_5,		KP_UP,		KP_8,		KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 3 */	KP_UNMAPPED,KP_UNMAPPED,KP_1,		KP_4,		KP_OK,		KP_7,		KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 4 */	KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_F3,		KP_UNMAPPED,KP_F0,		KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 5 */	KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 6 */	KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,	\
/*KYEOUT 7 */	KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,KP_UNMAPPED,	\
}

alt text

范例 参考下节“获取按键面板键值”。

C
(2)	获取按键面板键值
Tgt_extdrive_config.c
…
CONST UINT8 HAL_BOOT_RODATA g_tgtKeyMap[] = KEY_MAP;

函数原型  	
PUBLIC UINT8 tgt_GetKeyCode(UINT8 key);
头文件  
Tgt_extdrive_config.h
函数说明 
获取键值,该函数将调用 g_tgtKeyMap
返回值 
成功返回1。
说明 
该函数已内部封装调用,用户根据需求修改KEY_MAP即可。
C
// 在 pdt_app_cfg.h 中直接修改增益值
#define PDT_SPK_ANA_GAIN  (15) // 模拟增益 0~15
#define PDT_SPK_DIG_GAIN  (18) // 数字增益 0~31

// 语音播报增益表
extern unsigned char VASpkGainCN[7][2]; // 中文
extern unsigned char VASpkGainEN[7][2]; // 英文

10. GUI

alt text

GUI 模块基于 ATGUI 框架(如配置),提供以下功能:

  • 界面管理:窗口的创建、显示、隐藏

  • 控件管理:按钮、文本框、列表等控件

  • 动画效果:界面切换动画、状态指示动画

  • 事件处理:触摸事件、按键事件映射

GUI流程 usr_main.c - mmiTaskEntry() :

  1. ATGUI_StartUp(); 开机初始化LCD驱动,显示缓存,图片字库指针

  2. 创建GUI任务:

inst->guiTask = ATGUIInitInstance(inst->mmiTask, lcdguiEventGui2AppCb, inst);

→ lcdgui_EventGui2AppCb 回调函数, 返回消息到上层应用(在GUI菜单选择某些功能 进入某些功能页面,确定取消或者选择开关等)

→ 创建 ATGUIInstTaskEntry() 任务, 执行ATGUILCD_InstMsgHandler()处理GUI事件, 刷新新页面,页面跳转, 弹窗提示等

---

11. RF 协议栈

RF 协议栈基于 DMR 标准,提供:

  • 语音通信:数字语音编解码、传输、接收

  • 数据通信:短数据、长数据传输

  • 信令处理:呼叫建立、释放、漫游

  • 频道管理:信道切换、扫描、保持

---

协议栈文件位置

目录内容
ProCode/core/xcpuPlatform/协议栈固件(二进制,不可修改)
ProCode/core/bcpuPlatform/底层硬件抽象(不可修改)
ProCode/include/xstackheader.h协议栈消息定义头文件
ProCode/app/include/stackinterface.h协议栈接口头文件
ProCode/include/xstackheader.h协议栈事件定义

---

协议栈接口函数(代码中调用)

函数文件用途
xstackInitializer()usr_main.c初始化协议栈
usr_StackEventHandle()usr_stack.h协议栈事件处理回调
usr_StackSendChSwitch()usr_stack.h请求切换信道
usr_StackSendVoiceAnnouncement()usr_stack.h请求语音播报
usr_StackSendEvRxDisable()usr_stack.h禁用接收
usr_StackSendEvRxEnable()usr_stack.h启用接收

---

协议栈事件(xstackheader.h)

事件含义
XMSG_STACK_CHANGE_COMPLETE134信道切换完成
XMSG_STACK_DIGITAL_ALERT140数字告警
XMSG_STACK_CALLIN_START-来电开始
XMSG_STACK_CALLIN_END-来电结束
XMSG_STACK_SPK_ON-扬声器打开
XMSG_STACK_SPK_OFF-扬声器关闭
XMSG_VA_PLAY_STOP-语音播放停止

使用位置: App_ChannelChange.c / App_DataRx.c / AF_PSMsgFilter.c

---

用户代码与协议栈的交互流程

调用函数执行动作返回/回调事件处理机制
usr_StackSendChSwitch()触发协议栈执行信道切换XMSG_STACK_CHANGE_COMPLETE切换完成后,协议栈通过 PS_INPUT 机制向上层上报事件
usr_StackSendVA() / usr_StackSendVoiceAnnouncement()触发协议栈播放语音播报XMSG_VA_PLAY_STOP语音播放完成后,协议栈通过 PS_INPUT 机制向上层上报事件

协议栈是封装好的,只需调用接口函数、处理回调事件,不需要修改协议栈源码。

---

SDK 案例

1. GPIO 使用实例

来源:ProCode/app/a6hs/src/usr_key.c

旋钮中断初始化(GPIO7):

C
// 文件: usr_key.c → usr_KnodInitGpio()
static void usr_KnodInitGpio(void)
{
  HAL_GPIO_IRQ_MASK_T gpioIntMaskCfg;
  
  // 中断三步曲配置
  gpioIntMaskCfg.rising = TRUE;  // 上升沿
  gpioIntMaskCfg.falling = TRUE; // 下降沿
  gpioIntMaskCfg.debounce = TRUE; // 消抖
  gpioIntMaskCfg.level = FALSE;  // 边沿触发
  
  // 配置 4 个旋钮引脚为输入
  hal_GpioSetIn(KNOB_CHAN_GPIO_0); // GPIO7
  hal_GpioSetIn(KNOB_CHAN_GPIO_1); // GPIO12
  hal_GpioSetIn(KNOB_CHAN_GPIO_2); // GPIO14
  hal_GpioSetIn(KNOB_CHAN_GPIO_3); // GPIO15
  
  // 只有 GPIO7 满足中断条件(GPIO0~7)
  if (KNOB_CHAN_GPIO_0 >= HAL_GPIO_0 && KNOB_CHAN_GPIO_0 <= HAL_GPIO_7)
  {
    hal_GpioResetIrq(KNOB_CHAN_GPIO_0);             // 1. 清除中断
    hal_GpioIrqSetMask(KNOB_CHAN_GPIO_0, &gpioIntMaskCfg);    // 2. 设置触发
    hal_GpioIrqSetHandler(KNOB_CHAN_GPIO_0, usr_KnodIrq);    // 3. 注册回调
  }
}

旋钮中断回调(读电平判断方向):

C
// 文件: usr_key.c → usr_KnodIrq()
void usr_KnodIrq(void)
{
  if(pUsrHdl)
  {
    USR_MSG_t *knobEv = (USR_MSG_t *)pUsrHdl->os->zmalloc(sizeof(USR_MSG_t));
    // 比较 GPIO7 和 GPIO12 电平:1=顺时针,0=逆时针
    knobEv->nParam1 = (hal_GpioGet(KNOB_CHAN_GPIO_0) != hal_GpioGet(KNOB_CHAN_GPIO_1)) ? 1 : 0;
    pUsrHdl->os->postMessage(pUsrHdl->knobTask, (void *)knobEv);
  }
}

按键读取(轮询检测):

C
// 文件: usr_key.c → usr_programKeyDetection()
static void usr_programKeyDetection(void * arg)
{
  static unsigned char keyProgram1Bk = 1;
  static unsigned char key1Debounce = 0;
  
  keyProgram1 = hal_GpioGet(KEY_PROGRAM1_FUNC); // 读取 GPIO3
  
  if(keyProgram1 != keyProgram1Bk)    // 电平变化?
  {
    if(key1Debounce != 0)       // 消抖完成?
    {
      if(0 == keyProgram1)      // 按下(低电平)?
        usr_KeyMsg2Task(EV_KEY_RAW_DOWN, KP_F0, 0, 0, inst->mmiTask, inst);
      else
        usr_KeyMsg2Task(EV_KEY_RAW_UP, KP_F0, 0, 0, inst->mmiTask, inst);
      keyProgram1Bk = keyProgram1;
      key1Debounce = 0;
    }
    else
      key1Debounce++;        // 消抖计数中
  }
  else
    key1Debounce = 0;
}

整旋钮切换信道流程

来源:ProCode/app/a6hs/src/usr_key.c

C
// 步骤 1:旋钮旋转 → 触发中断
void usr_KnodIrq(void)
{
  USR_MSG_t *knobEv = pUsrHdl->os->zmalloc(sizeof(USR_MSG_t));
  knobEv->nParam1 = (hal_GpioGet(GPIO7) != hal_GpioGet(GPIO12)) ? 1 : 0;
  pUsrHdl->os->postMessage(pUsrHdl->knobTask, knobEv);
}

// 步骤 2:旋钮任务处理
void * knobTaskEntry(void *arg)
{
  inst->os->pendMessage(inst->knobTask, &ev_ptr, 1); // 等待消息
  USR_MSG_t *pMsg = (USR_MSG_t *)ev_ptr;
  channelSwitchDetection(inst, pMsg->nParam1);    // 处理切换
  Usr_KeySendMsgToMainTask(inst, KEY_FUNC_KNOB, BUTTON_CODE_CHANNEL);
}

// 步骤 3:信道切换处理
void channelSwitchDetection(void *arg, uint32_t rotate)
{
  // 无极旋钮模式(CH_SWITCH_KNOB_STEPLESS)
  if(rotate == 1) // 顺时针
  {
    usrKnodNum++;
    if(usrKnodNum >= maxChannelNum) usrKnodNum = 0;
  }
  else // 逆时针
  {
    if(usrKnodNum == 0) usrKnodNum = maxChannelNum - 1;
    else usrKnodNum--;
  }
}

// 步骤 4:通知协议栈切换频率
void channel_switching(void *arg, int curChannel)
{
  chPara.CHPublicPara.rxFreq = pCh->nRxFreq;
  chPara.CHPublicPara.txFreq = pCh->nTxFreq;
  usr_StackSendChSwitch(inst, curChannel, CH_MODE_DMR, &chPara); // 调用协议栈
}

---

3. 按键使用实例

来源:ProCode/app/a6hs/src/usr_key.c

按键初始化:

C
// 文件: usr_key.c → usr_KeyInit()
void usr_KeyInit(void)
{
  hal_GpioSetIn(PTT_GPIO);       // GPIO1
  hal_GpioSetIn(KEY_PROGRAM1_FUNC);   // GPIO3
  hal_GpioSetIn(KEY_PROGRAM2_FUNC);   // GPIO6
  hal_GpioSetIn(KEY_EMERGENCY);     // GPIO5
}

发送按键消息:

C
// 文件: usr_key.c → usr_KeyMsg2Task()
void 
usr_KeyMsg2Task(
	UINT32 msgId, 
	UINT32 p1, 
	UINT32 p2, 
	UINT32 p3, 
	void *task, 
	void *arg)
{
	struct usrInstance_t *inst = (struct usrInstance_t *)arg;
	KEY_EVENT_T *ev_to_send = NULL; 

	ev_to_send = (KEY_EVENT_T *)inst->os->zmalloc(sizeof(KEY_EVENT_T));			
	PRINT_ASSERT(ev_to_send, MEM_MALLOC_FAILED);
	ev_to_send->id = msgId;
	ev_to_send->par1 = p1;
	ev_to_send->par2 = p2;
	ev_to_send->par3 = p3;
	inst->os->postMessage(task, (void *)ev_to_send);
}

按键任务(50ms 轮询):

C
// 文件: usr_key.c → keyTaskEntry()
void * keyTaskEntry(void *arg)
{
  struct usrInstance_t *inst = arg;
  pUsrHdl = inst;
  usr_KeyInit();             // 初始化按键
  
  // 创建 50ms 定时器
  inst->os->createTimer(&inst->keyTaskCheckTimer, keyTaskCheckCb, inst, 50);
  inst->os->startTimer(inst->keyTaskCheckTimer);
  
  while(1)
  {
    inst->os->pauseTask(inst->keyTask); // 睡眠等唤醒
    usr_programKeyDetection(inst);   // 轮询检测
    usr_pttProcessHandle(inst);     // PTT 检测
    usr_earPhoneCheck(inst);      // 耳机检测
    usrVolKnob_GetVal(inst);      // 音量旋钮
  }
}

4. LCD控制实例

示例:LED 控制

范例1:LCD驱动注册 - lcdd_st7735r_RegInit()

文件位置: ProCode/appFramework/extDriver/lcdd/st7735r/slcd/src/drv_lcd_st7735r.c

C
#include "lcdd.h"
#include "lcdd_types.h"

PUBLIC BOOL lcdd_st7735r_RegInit(LCDD_REG_T *pLcdDrv)
{ 
  if( lcddp_CheckProductId())
  {
    pLcdDrv->lcdd_Open=lcddp_Open;
    pLcdDrv->lcdd_Close=lcddp_Close;
    pLcdDrv->lcdd_SetContrast=lcddp_SetContrast;
 //    
    pLcdDrv->lcdd_SetStandbyMode=lcddp_SetStandbyMode;
    pLcdDrv->lcdd_PartialOn=lcddp_PartialOn;
    pLcdDrv->lcdd_PartialOff=lcddp_PartialOff;
    pLcdDrv->lcdd_Blit16=lcddp_Blit16;
    pLcdDrv->lcdd_Busy=lcddp_Busy;
    pLcdDrv->lcdd_FillRect16=lcddp_FillRect16;
    pLcdDrv->lcdd_GetScreenInfo=lcddp_GetScreenInfo;
    pLcdDrv->lcdd_WakeUp=lcddp_WakeUp;
    pLcdDrv->lcdd_SetPixel16=lcddp_SetPixel16;
    pLcdDrv->lcdd_Sleep=lcddp_Sleep;
    pLcdDrv->lcdd_SetDirRotation=lcddp_SetDirRotation;
    pLcdDrv->lcdd_SetDirDefault=lcddp_SetDirDefault;		
    pLcdDrv->lcdd_GetStringId=lcdd_get_id_string;

    return TRUE;
  }

  return FALSE;
}

---

范例2:使用LCD API - 打开、显示、关闭

文件位置: ProCode/appFramework/UiSystem/src/atgui_inst.cusr_gui.c ProCode/appFramework/extDriver/lcdd/st7735r/gouda/src/drvlcdst7735r.c /ProCode/appFramework/extDriver/lcdd/st7735r/slcd/src/drvlcdst7735r.c /ProCode/appFramework/UiSystem/src/GUI_User.c

C
#include "lcdd.h"
#include "lcdd_m.h"

// 1. 打开LCD
LCDD_ERR_T ret;
if(LCDD_ERR_NO != lcdd_Open())
{
  MMI_TRACE(3, "lcdd_Open error");
  return;
}
MMI_TRACE(3, "lcdd_Open Ok");

// 2. 获取屏幕信息
LCDD_SCREEN_INFO_T screenInfo;
lcdd_GetScreenInfo(&screenInfo);
// screenInfo.width = 128, screenInfo.height = 160, screenInfo.bitdepth = 16

// 3. 填充整个屏幕为白色背景
LCDD_ROI_T roi;
roi.x = 0;
roi.y = 0;
roi.width = screenInfo.width;
roi.height = screenInfo.height;
lcdd_FillRect16(&roi, 0xFFFF); // 0xFFFF = 白色

// 4. 位块传输(显示图像/数据到LCD)
LCDD_FBW_T frameBufferWin;
frameBufferWin.fb.buffer = myImageBuffer;    // 图像数据缓冲区
frameBufferWin.fb.width = 128;          // 缓冲区宽度
frameBufferWin.fb.height = 160;          // 缓冲区高度
frameBufferWin.fb.colorFormat = LCDD_COLOR_FORMAT_RGB_565;
frameBufferWin.roi.x = 0;
frameBufferWin.roi.y = 0;
frameBufferWin.roi.width = 128;
frameBufferWin.roi.height = 160;

ret = lcdd_Blit16(&frameBufferWin, 0, 0); // 从(0,0)开始显示

// 5. 关闭LCD
lcdd_Close();

---

范例3:LCD底层实现 - lcddp_Open()lcddp_Blit16()

文件位置: ProCode/appFramework/extDriver/lcdd/st7735r/slcd/src/drv_lcd_st7735r.c

C
// LCD打开函数(底层实现)
PRIVATE LCDD_ERR_T lcddp_Open(VOID)
{
  // 1. 开启LCD电源
  pmd_EnablePower(PMD_POWER_LCD, TRUE);
  
  // 2. 配置SPI引脚
  hal_SpiForcePin(g_lcdSpiId, HAL_SPI_PIN_CLK, HAL_SPI_PIN_STATE_SPI);
  hal_SpiForcePin(g_lcdSpiId, HAL_SPI_PIN_DO, HAL_SPI_PIN_STATE_SPI);
  
  // 3. 获取RST和A0引脚配置
  g_slcd_rst = tgt_GetSpiLcdConfig()->lcdResetPin;
  g_slcd_a0 = tgt_GetSpiLcdConfig()->lcdA0Pin;
  
  // 4. 配置GPIO
  if(g_slcd_a0.type == HAL_GPIO_TYPE_IO)
    hal_GpioSetOut(g_slcd_a0.gpioId);
  if(g_slcd_rst.type == HAL_GPIO_TYPE_IO)
    hal_GpioSetOut(g_slcd_rst.gpioId);
  
  // 5. LCD复位序列
  sxr_Sleep(5 * 1);
  hal_GpioSet(g_slcd_rst);
  sxr_Sleep(10 * 1);
  hal_GpioClr(g_slcd_rst);
  sxr_Sleep(5 * 1);
  hal_GpioSet(g_slcd_rst);
  sxr_Sleep(50 * 1);
  
  // 6. 打开SPI
  lcddp_Open_Spi();
  
  // 7. 初始化LCD控制器(发送ST7735R初始化命令)
  lcddp_Init();
  
  return LCDD_ERR_NO;
}

// LCD位块传输(底层实现)
PRIVATE LCDD_ERR_T lcddp_Blit16(CONST LCDD_FBW_T* frameBufferWin, UINT16 startX, UINT16 startY)
{
  HAL_GOUDA_WINDOW_T activeWin;
  
  if(0 == lcdd_MutexGet())
    return LCDD_ERR_RESOURCE_BUSY;
  
  if(g_lcddInSleep)
  {
    lcdd_MutexFree();
    return LCDD_ERR_NO;
  }
  
  // 设置活动窗口坐标
  activeWin.tlPX = startX;
  activeWin.brPX = startX + frameBufferWin->roi.width - 1;
  activeWin.tlPY = startY;
  activeWin.brPY = startY + frameBufferWin->roi.height - 1;
  
  // 设置LCD列地址
  WriteCommand_Addr(0x2a);
  WriteCommand_Data(0x00);
  WriteCommand_Data(activeWin.tlPX);
  WriteCommand_Data(0x00);
  WriteCommand_Data(activeWin.brPX);
  
  // 设置LCD行地址
  WriteCommand_Addr(0x2b);
  WriteCommand_Data(0x00);
  WriteCommand_Data(activeWin.tlPY);
  WriteCommand_Data(0x00);
  WriteCommand_Data(activeWin.brPY);
  
  // 发送像素数据命令
  WriteCommand_Addr(0x2c);
  hal_GpioSet(g_slcd_a0);
  
  // 通过SPI发送像素数据
  lcdd_TransferData(frameBufferWin->fb.buffer, 
           frameBufferWin->roi.width * frameBufferWin->roi.height, 
           TRUE);
  
  return LCDD_ERR_NO;
}

---

范例4:测试显示函数

文件位置: ProCode/appFramework/extDriver/lcdd/st7735r/slcd/src/drv_lcd_st7735r.c

C
VOID test_display_sensor(UINT16 * temp)
{
  LCDD_ERR_T ret;
  LCDD_SCREEN_INFO_T screenInfo;
  LCDD_ROI_T roi;
  LCDD_FBW_T frameBufferWin;
  
  // 获取屏幕信息
  lcdd_GetScreenInfo(&screenInfo);
  
  // 设置ROI(感兴趣区域)
  roi.x = 0;
  roi.y = 0;
  roi.width = screenInfo.width;
  roi.height = screenInfo.height;
  
  // 设置帧缓冲区
  frameBufferWin.fb.buffer = temp;          // 图像数据
  frameBufferWin.fb.width = 128;
  frameBufferWin.fb.height = 160;
  frameBufferWin.fb.colorFormat = LCDD_COLOR_FORMAT_RGB_565;
  
  frameBufferWin.roi.x = 0;
  frameBufferWin.roi.y = 0;
  frameBufferWin.roi.width = 128;
  frameBufferWin.roi.height = 160;
  
  // 显示图像
  ret = lcddp_Blit16(&frameBufferWin, 0, 0);
}

---

LCD数据类型定义

C
// LCDD_SCREEN_INFO_T - 屏幕信息
typedef struct
{
  UINT16 width;      // 宽度 (128)
  UINT16 height;     // 高度 (160)
  UINT32 bitdepth;    // 位深 (LCDD_COLOR_FORMAT_RGB_565 = 16bit)
  UINT32 nReserved;
} LCDD_SCREEN_INFO_T;

// LCDD_ROI_T - 感兴趣区域(矩形区域)
typedef struct
{
  UINT16 x;        // 起始X坐标
  UINT16 y;        // 起始Y坐标
  UINT16 width;      // 宽度
  UINT16 height;     // 高度
} LCDD_ROI_T;

// LCDD_FBW_T - 帧缓冲区窗口
typedef struct
{
  LCDD_FB_T fb;      // 帧缓冲区(buffer, width, height, colorFormat)
  LCDD_ROI_T roi;     // 感兴趣区域
} LCDD_FBW_T;

// LCDD_REG_T - LCD驱动函数注册表(用户要配置的结构体)
typedef struct 
{ 
  LCDD_ERR_T (*lcdd_Open)(VOID);           // 打开
  LCDD_ERR_T (*lcdd_Close)(VOID);          // 关闭
  LCDD_ERR_T (*lcdd_SetContrast)(UINT32 contrast);  // 对比度
  LCDD_ERR_T (*lcdd_SetStandbyMode)(BOOL standbyMode); // 待机
  LCDD_ERR_T (*lcdd_Sleep)(VOID);          // 睡眠
  LCDD_ERR_T (*lcdd_WakeUp)(VOID);          // 唤醒
  LCDD_ERR_T (*lcdd_Blit16)(CONST LCDD_FBW_T*, UINT16, UINT16); // 显示
  LCDD_ERR_T (*lcdd_FillRect16)(CONST LCDD_ROI_T*, UINT16);   // 填充
  BOOL (*lcdd_Busy)(VOID);              // 忙
  BOOL (*lcdd_SetDirRotation)(VOID);         // 旋转
  BOOL (*lcdd_SetDirDefault)(VOID);         // 默认
  char* (*lcdd_GetStringId)(VOID);          // 获取ID
} LCDD_REG_T;

LCD支持的驱动芯片列表

项目中支持大量LCD驱动芯片,在 lcdd.h 中声明:

驱动芯片注册/初始化函数
ST7735Rlcdd_st7735r_RegInit()
ILI9341lcdd_ili9341_RegInit()
ILI9325lcdd_ili9325_RegInit()
SSD1297lcdd_ssd1297_RegInit()
HX8347Dlcdd_hx8347d_RegInit()
GC9301lcdd_gc9301ds_RegInit()
RM68130lcdd_rm68130_RegInit()
...等30+种依具体芯片型号而定

---

5. UART

示例:UART 数据发送

C
#include "uart.h"

#define UART_PORT  UART_PORT_1
#define BAUDRATE   UART_BAUD_115200

// UART 初始化
void UART_InitExample(void)
{
  UART_Init(UART_PORT, BAUDRATE);
}

// 发送字符串
void UART_SendString(const char *str)
{
  while (*str) {
    UART_Send(UART_PORT, (uint8_t *)(str++), 1);
  }
}

// 发送二进制数据
void UART_SendData(const uint8_t *data, uint32_t len)
{
  UART_Send(UART_PORT, (uint8_t *)data, len);
}

示例:UART 中断接收

C
static void UartRxCallback(uint8_t data)
{
  // 将接收到的数据存入缓冲区
  RingBuffer_Put(&rxBuffer, data);
  
  // 可在此触发消息通知
  App_SendEvent(UART_INPUT, data);
}

void UART_InterruptInit(void)
{
  UART_Init(UART_PORT, BAUDRATE);
  UART_SetCallback(UART_PORT, UartRxCallback);
}

范例1:串口配置 + 发送 + 接收(来自 atecps_entry.c

C
#include "uc_uart.h"

// 1. 串口配置
static HAL_UART_ID_T uartId = HAL_UART_1;
static int uartBaudrate = HAL_UART_BAUD_RATE_9600;

// 串口配置函数
uartConfigure(uartId, uartBaudrate, inst->atecpsTask);

// 2. 串口发送数据
void atecps_UartTxData(unsigned char *txBuf, unsigned int txLen)
{
  if (NIL != txBuf && 0 < txLen)
  {
    RTOS_t *os = RTOS_Init();
    // 等待串口发送空闲
    while(uartIsTxIdle(uartId) == FALSE)
      os->sleepTask(30);
    
    /* 发送数据到串口 */
    uartWrite(uartId, (void *)txBuf, txLen);
  }
}

// 3. 串口接收数据(在任务循环中)
static void * atecps_TaskEntry(void *arg)
{
  struct usrInstance_t *inst = arg;
  serialPort_t* port = GetSerialPort(PORTTYPE_UART);
  
  // 配置串口
  uartConfigure(uartId, uartBaudrate, inst->atecpsTask);
  
  inst->os->sleepTask(200);
  
  while(1)
  {
    char rx_data1 = 0;
    char rx_data2 = 0;
    
    inst->os->pauseTask(inst->atecpsTask);
    
    // 从UART1接收数据(单字节循环读取)
    while(0 != uartRead(HAL_UART_1, (void *)&rx_data1, 1))
    {
      atecps_dataHandle(port, &rx_data1, 1);
    }
    
    // 从UART2接收数据
    while(0 != uartRead(HAL_UART_2, (void *)&rx_data2, 1))
    {
      atecps_dataHandle(port, &rx_data2, 1);
    }
  }
  return (void *)0;
}

---

范例2:串口波特率切换(来自 atecps_entry.c

C
int atecps_UartRateChange(unsigned int baudRate)
{
  struct usrInstance_t *inst = usr_GetInst();
  
  // 等待串口发送空闲
  while (uartIsTxIdle(uartId) == FALSE)
    RTOS_Init()->sleepTask(100);
  
  // 波特率范围处理
  if (baudRate == 0) 
    baudRate = HAL_UART_BAUD_RATE_9600;
  else if (baudRate >= HAL_UART_BAUD_RATE_921600)
    baudRate = HAL_UART_BAUD_RATE_921600;
  else if (baudRate >= HAL_UART_BAUD_RATE_460800)
    baudRate = HAL_UART_BAUD_RATE_460800;
  else if (baudRate >= HAL_UART_BAUD_RATE_230400)
    baudRate = HAL_UART_BAUD_RATE_230400;
  else if (baudRate >= HAL_UART_BAUD_RATE_115200)
    baudRate = HAL_UART_BAUD_RATE_115200;
  else if (baudRate >= HAL_UART_BAUD_RATE_57600)
    baudRate = HAL_UART_BAUD_RATE_57600;
  else if (baudRate >= HAL_UART_BAUD_RATE_38400)
    baudRate = HAL_UART_BAUD_RATE_38400;
  else if (baudRate >= HAL_UART_BAUD_RATE_19200)
    baudRate = HAL_UART_BAUD_RATE_19200;
  else 
    baudRate = HAL_UART_BAUD_RATE_9600;
  
  if (baudRate != uartBaudrate)
  {
    uartBaudrate = baudRate;
    // 重新配置串口(波特率变更后必须重新配置)
    uartConfigure(uartId, uartBaudrate, inst->atecpsTask);
  }
  return uartBaudrate;
}

---

范例3:串口音频/语音收发(来自 atecps_entry.cUART_VOICE 模式)

C
#if defined(UART_VOICE)
// 定义ATE串口和语音串口
HAL_UART_ID_T ateUartId = HAL_UART_1;   // ATE串口
HAL_UART_ID_T voiceUartId = HAL_UART_2;  // 语音串口
int voiceUartBaudrate = HAL_UART_BAUD_RATE_460800; // 语音串口波特率
int ateUartBaudrate = HAL_UART_BAUD_RATE_9600;   // ATE串口波特率

// 配置语音串口
void AudPcmInfo_uart_config(void)
{
  struct usrInstance_t *inst = p_atecps_inst;
  uartConfigure(uartId, uartBaudrate, inst->atecpsTask);
}

// 通过串口发送语音PCM数据
void TxVoicePCMDataToUart(unsigned char *txBuf, unsigned int txLen)
{
  if (NIL != txBuf && 0 < txLen)
  {
    RTOS_t *os = RTOS_Init();
    while(uartIsTxIdle(voiceUartId) == FALSE)
      os->sleepTask(30);
    uartWrite(voiceUartId, (void *)txBuf, txLen);
  }
}

// 任务中同时处理两个串口
static void * atecps_TaskEntry(void *arg)
{
  struct usrInstance_t *inst = arg;
  char rx_data1 = 0; // ATE串口数据
  char rx_data2 = 0; // 语音串口数据
  
  // 配置ATE串口
  if(ateUartId != 0)
    uartConfigure(ateUartId, ateUartBaudrate, inst->atecpsTask);
  
  // 配置语音串口
  if(voiceUartId != 0)
    uartConfigure(voiceUartId, voiceUartBaudrate, inst->atecpsTask);
  
  while(1)
  {
    inst->os->pauseTask(inst->atecpsTask);
    
    // 读取ATE串口数据
    if(ateUartId != 0)
    {
      while(0 != uartRead(ateUartId, (void *)&rx_data1, 1))
      {
        atecps_dataHandle(port, &rx_data1, 1);
      }
    }
    
    // 读取语音串口数据
    if(voiceUartId != 0)
    {
      while(0 != uartRead(voiceUartId, (void *)&rx_data2, 1))
      {
        RxAudPcm_parse(rx_data2); // 解析语音帧
      }
    }
  }
}
#endif

---

范例4:蓝牙串口(来自 usr_BT.c

C
#include "uc_uart.h"

#define BT_UART_DEFAULT   ((HAL_UART_ID_T)(BT_PORT.uart_X))
#define BT_BOUDRATE_DEFAULT ((HAL_UART_BAUD_RATE_T)(BT_PORT.uart_boud))

// 蓝牙串口初始化
static void bt_uartInit(void *rxCBs)
{
  uartConfigure(BT_UART_DEFAULT, BT_BOUDRATE_DEFAULT, rxCBs);
}

// 蓝牙串口发送
int bt_uartWrite(uint8_t *buf, int len)
{
  return uartWrite(BT_UART_DEFAULT, buf, len);
}

// 蓝牙串口接收(在蓝牙任务中)
static void * btTaskEntry(void *arg)
{
  unsigned char rxByte;
  
  bt_uartInit(rxCBs);
  bt_gpioInit();
  
  while(1)
  {
    // 循环读取蓝牙串口数据
    while(0 != uartRead(BT_UART_DEFAULT, (void *)&rxByte, 1))
    {
      // 处理接收到的蓝牙数据
      // ...
    }
  }
}

---

范例5:设备初始化时配置所有串口(来自 usr_device_cfg.c

C
#include "hal_uart.h"

typedef struct
{
  ENABLE_STATUS_t uart1Enable;
  ENABLE_STATUS_t uart2Enable;
  ENABLE_STATUS_t uart3Enable;
  ENABLE_STATUS_t uart4Enable;
} a6devCfg_t;

// 串口初始化
a6devCfg.uart1Enable = ENABLE;
a6devCfg.uart2Enable = DISABLE;
a6devCfg.uart3Enable = DISABLE;
a6devCfg.uart4Enable = DISABLE;

// 使能UART1
if(ENABLE == a6devCfg.uart1Enable)
{
  uartConfigure(HAL_UART_1, 9600, NULL);
}

// 使能UART2
if(ENABLE == a6devCfg.uart2Enable)
{
  uartConfigure(HAL_UART_2, 9600, NULL);
}

// 关闭串口
if(ENABLE == a6devCfg.uart1Enable)
{
  hal_UartClose(HAL_UART_1);
}

---

? 串口 API 函数汇总

函数头文件说明
uartConfigure(port, baudrate, task)uc_uart.h配置串口(端口、波特率、接收任务)
uartWrite(port, buf, len)uc_uart.h串口发送数据,成功返回写入字节数
uartRead(port, buf, len)uc_uart.h串口接收数据,成功返回读取字节数
uartIsTxIdle(port)uc_uart.h检查串口是否发送空闲
uartEnableRxIrq(port, enable)uc_uart.h使能/禁止串口接收中断
uartEnableTxIrq(port, enable)uc_uart.h使能/禁止串口发送中断
uartClose(port)uc_uart.h关闭串口
hal_UartClose(port)hal_uart.h底层关闭串口
hal_UartSetBaudRate(port, mode, rate)hal_uart.h底层设置波特率

---

6. MIC/SPK 增益配置

来源:pdt_app_cfg.h

C
// 文件: pdt_app_cfg.h → 增益配置
// MIC 模拟增益 0~7
#define PDT_MIC_ANA_GAIN  (3)
// MIC 数字增益 0~15
#define PDT_MIC_DIG_GAIN  (4)
// SPK 模拟增益 0~15
#define PDT_SPK_ANA_GAIN  (15)
// SPK 数字增益 0~31
#define PDT_SPK_DIG_GAIN  (18)

// 在代码中设置增益(usr_main.c)
usr_setMicSpkGainDefault(inst);  // 设置 MIC 默认增益
usr_setVASpkGain(inst);      // 设置 SPK 默认增益
usr_setVolume(inst, inst->volume); // 设置音量

---

©2022 力同科技股份有限公司   粤ICP备12028371号-1    Sitemap