所有的外设都有一套共用的初始化流程
外设驱动函数操作外设的实质是对寄存器的读写操作
配置外设
- 过程:XScuTimer_LookupConfig函数根据ID查找XScuTimer_ConfigTable 中对应的配置表项并返回,其中TIMER_DEVICE_ID 对应寄存器基址。
宏定义 TIMER_DEVICE_ID 为 SDK 为这个定时器分配的 ID 号,分配 ID 号的原因是因为同种类型的外设数量不止一个。比如我们上篇文章中说过:乐意的话完全可以配置 30 路串口,那么 SDK 就会为这 30 个串口从 0 开始分配 ID。在初始化和使用这些串口的过程中,都需要讲清楚要使用哪个 ID 所对应的驱动。
XScuTimer_Config *XScuTimer_LookupConfig(u16 DeviceId)
{
XScuTimer_Config *CfgPtr = NULL;
u32 Index;
for (Index = 0U; Index < XPAR_XSCUTIMER_NUM_INSTANCES; Index++) {
if (XScuTimer_ConfigTable[Index].DeviceId == DeviceId) {
CfgPtr = &XScuTimer_ConfigTable[Index];
break;
}
}
return (XScuTimer_Config *)CfgPtr;
}
定时器配置结构体如下:
/**
* This typedef contains configuration information for the device.
*/
typedef struct {
u16 DeviceId; /**< Unique ID of device */
u32 BaseAddr; /**< Base address of the device */
} XScuTimer_Config;
外设配置信息,由硬件生成,对于私有定时器外设来说其实只有外设寄存器基址。
XScuTimer_Config XScuTimer_ConfigTable[XPAR_XSCUTIMER_NUM_INSTANCES] =
{
{
XPAR_PS7_SCUTIMER_0_DEVICE_ID,
XPAR_PS7_SCUTIMER_0_BASEADDR
}
};
初始化
- 过程:XScuTimer_CfgInitialize 使用配置结构体作为参数
- 将外设实例地址初始化配置信息里的地址
- 将外设实例的状态变量进行初始化 Timer结构体
typedef struct {
XScuTimer_Config Config; /**< Hardware Configuration */
u32 IsReady; /**< Device is initialized and ready */
u32 IsStarted; /**< Device timer is running */
} XScuTimer;
初始化函数如下:
s32 XScuTimer_CfgInitialize(XScuTimer *InstancePtr,
XScuTimer_Config *ConfigPtr, u32 EffectiveAddress)
{
s32 Status;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(ConfigPtr != NULL);
if (InstancePtr->IsStarted != XIL_COMPONENT_IS_STARTED) {
InstancePtr->Config.DeviceId = ConfigPtr->DeviceId;
InstancePtr->Config.BaseAddr = EffectiveAddress;
InstancePtr->IsStarted = (u32)0;
InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
Status =(s32)XST_SUCCESS;
}
else {
Status = (s32)XST_DEVICE_IS_STARTED;
}
return Status;
}
XScuTimer_CfgInitialize 函数中首先通过 Xil_AssertNonvoid 函数判断传入的结构体参数是否为空。之后判断外设是否处于启动状态,如果仍处于启动状态,直接初始化未免不合适,这里返回 XST_DEVICE_IS_STARTED 作为错误代码。上层函数根据错误代码进行判断,先停止外设再初始化为好。
所有针对定时器的操作,包括配置,启动,停止函数都需要传入 XScuTimer 结构体指针作为参数。
配置
嵌入式寄存器操作
宏定义形式:
#define XScuTimer_EnableAutoReload(InstancePtr) \
XScuTimer_WriteReg((InstancePtr)->Config.BaseAddr, \
XSCUTIMER_CONTROL_OFFSET, \
(XScuTimer_ReadReg((InstancePtr)->Config.BaseAddr, \
XSCUTIMER_CONTROL_OFFSET) | \
XSCUTIMER_CONTROL_AUTO_RELOAD_MASK))
XSCUTIMER_CONTROL 寄存器,地址为 BaseAddr + XSCUTIMER_CONTROL_OFFSET
操作
void XScuTimer_Start(XScuTimer *InstancePtr)
{
u32 Register;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* Read the contents of the Control register.
*/
Register = XScuTimer_ReadReg(InstancePtr->Config.BaseAddr,
XSCUTIMER_CONTROL_OFFSET);
/*
* Set the 'timer enable' bit in the register.
*/
Register |= XSCUTIMER_CONTROL_ENABLE_MASK;
/*
* Update the Control register with the new value.
*/
XScuTimer_WriteReg(InstancePtr->Config.BaseAddr,
XSCUTIMER_CONTROL_OFFSET, Register);
/*
* Indicate that the device is started.
*/
InstancePtr->IsStarted = XIL_COMPONENT_IS_STARTED;
}
这里也提醒我们,在使用宏定义形式的配置函数之前,最好先对传入的参数进行检查。毕竟写错地址,写到 0 地址,可能是个大问题。