D:\Xilinx\SDK\2019.1\data\embeddedsw\XilinxProcessorIPLib\drivers\uartlite_v3_2\examples
中的
xuartlite_low_level_example.c文件。作为移植的参考文件。
这个文件中,
设置了静态的变量,
u8 SendBuffer[TEST_BUFFER_SIZE]; /* Buffer for Transmitting Data */
u8 RecvBuffer[TEST_BUFFER_SIZE]; /* Buffer for Receiving Data */
这是我们需要封装的数据。
在C++环境下,既可以封装为class,也可以封装为struct,为了尽量多的保持SOD风格,这里封装成struct。
并为它设计相应的功能级函数。
来看main中的函数,UartLiteLowLevelExample,这是主功能体。
这个函数中,填充了send_buffer,并调用XUartLite_SendByte函数,逐个byte的发送出去。
调用了XUartLite_RecvByte函数,逐个byte的接收数据。
我们的模块中,send可以主动发送,而receive则需要在借助中断来触发执行。
所以,low_level例子中的send可以移植,而receive则需要从
xuartlite_intr_example.c文件中移植。
来看看这个例子。
这个例子中,由几个static变量。
XUartLite UartLite; /* The instance of the UartLite Device */
XIntc InterruptController; /* The instance of the Interrupt Controller */
static volatile int TotalReceivedCount;
这是我们需要封装的变量。
来看看main中,UartLiteIntrExample是主功能体。
这个函数中,首先初始化了uartlite,XUartLite_Initialize函数完成这个任务,
然后selftest,
然后配置中断,SetupInterruptSystem函数完成这个任务,
来看看SetupInterruptSystem,和所有的类似例子一样,这个函数,初始化INTC,然后xintc_connect,xintc_start,xintc_enable,然后初始化exception,exception_register,exception_enable,这些操作都是整板级别的处理,
这里,需要重点关注的,是xintc_connect,它将uart的handler挂载到intc中。即
XUartLite_InterruptHandler函数,这是BSP包提供的函数。
Status = XIntc_Connect(&InterruptController, UARTLITE_INT_IRQ_ID,
(XInterruptHandler)XUartLite_InterruptHandler,
(void *)UartLitePtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
在这个函数里,会调用两个static的函数,进行收发数据的处理,例如
static void ReceiveDataHandler(XUartLite *InstancePtr),
这个函数里,会调用XUartLite_ReceiveBuffer函数,继续接收数据,
如果请求接收的数据已经全部接收完成,那么会调用xuartlite所注册的callback,进行接收完成的后处理。
所以,callback的定位,就是作为接收完成的后处理函数存在。
typedef struct {
XUartLite_Stats Stats; /* Component Statistics */
UINTPTR RegBaseAddress; /* Base address of registers */
u32 IsReady; /* Device is initialized and ready */
XUartLite_Buffer SendBuffer;
XUartLite_Buffer ReceiveBuffer;
XUartLite_Handler RecvHandler;
void *RecvCallBackRef; /* Callback ref for recv handler */
XUartLite_Handler SendHandler;
void *SendCallBackRef; /* Callback ref for send handler */
} XUartLite;
typedef void (*XUartLite_Handler)(void *CallBackRef, unsigned int ByteCount);
回到UartLiteIntrExample函数中,
接下来,是XUartLite_SetSendHandler函数,和XUartLite_SetRecvHandler函数。这是BSP提供的函数,用来设置xuartlite中的callback和callbackref。
XUartLite_SetRecvHandler(&UartLite, RecvHandler, &UartLite);
这个callback,是我们需要移植的函数。
接下来,是XUartLite_EnableInterrupt函数,这是BSP提供的函数,用来使能uart中断。
接下来,是XUartLite_Recv函数,这是BSP提供的函数,用来启动一次receive,它对UART的寄存器进行读写,并配置接收的数据存放的buffer的指针。
然后,主线程作为后台,ISR和callback作为前台,前后台通过共享变量进行通信,
当ISR中判断,全部数据都被接收时,触发callback,在callback被触发时,修改共享变量,
后台中,轮询共享变量,并自旋等待,当判断共享变量值被修改为所需要的值时,跳出等待。
void RecvHandler(void *CallBackRef, unsigned int EventData)
{
TotalReceivedCount = EventData;
}
int UartLiteIntrExample(u16 DeviceId)
{
while (TotalSentCount != TEST_BUFFER_SIZE) { }
}
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
下面来看看具体怎么封装的。
我们需要UART对FIFO进行接收,所以自己设计了ringbuffer。
struct UartLiteDev
{
UINTPTR regs;
struct RingBuffer recv_buff;
u32 irq_count;
};
struct RingBuffer
{
int wptr;
int rptr;
unsigned char data[MAX_RNG_BUF_SIZE];
int max_size;
};
配套的,我们设计了对ringbuffer的操作函数,将ringbuffer作为FIFO使用,
void ring_buffer_init(struct RingBuffer* ring, int size)
{
ring->wptr = 0;
ring->rptr = 0;
ring->max_size = size;
}
void ring_buffer_clear(struct RingBuffer* ring)
{
ring->wptr = 0;
ring->rptr = 0;
}
int ring_buffer_is_empty(struct RingBuffer* ring)
{
if ((ring->wptr % ring->max_size) == (ring->rptr % ring->max_size))
return 1;
return 0;
}
int ring_buffer_is_full(struct RingBuffer* ring)
{
if (((ring->wptr + 1) % ring->max_size) == (ring->rptr % ring->max_size))
return 1;
return 0;
}
int ring_buffer_push(struct RingBuffer* ring, unsigned char value)
{
if (ring_buffer_is_full(ring))
return -1;
ring->data[ring->wptr % ring->max_size] = value;
ring->wptr++;
return 0;
}
int ring_buffer_pop(struct RingBuffer* ring, unsigned char *value)
{
if (ring_buffer_is_empty(ring))
return -1;
*value = ring->data[ring->rptr % ring->max_size];
ring->rptr++;
return 0;
}
int ring_buffer_valid_len(struct RingBuffer* ring)
{
int rptr = ring->rptr % ring->max_size;
int wptr = ring->wptr % ring->max_size;
if (wptr >= rptr)
return (wptr - rptr);
else
return (wptr + ring->max_size - rptr);
}
int ring_buffer_get_value(struct RingBuffer* ring, int index, unsigned char* value)
{
if ((index < 0) || (index >= ring_buffer_valid_len(ring)))
return -1;
*value = ring->data[(ring->rptr + index) % ring->max_size];
return 0;
}
其中有初始化,清零,空满判断,压队出队,索引取值等操作。
可以看到,在uartlitedev中,我们内嵌了一个ringbuffer。
对于uartlitedev,
配套的操作函数是
void uart_lite_init(struct UartLiteDev* dev, u32 regs);
void uart_lite_start(struct UartLiteDev* dev);
void uart_lite_send(struct UartLiteDev* dev, u8* buf, int len);
void uart_lite_isr_handle(struct UartLiteDev* dev);
先来看inti。
void uart_lite_init(struct UartLiteDev* dev, u32 regs)
{
dev->regs = regs;
dev->irq_count = 0;
//初始化环形数组
ring_buffer_init(&dev->recv_buff, MAX_RNG_BUF_SIZE);
/* Write to the control register to disable the interrupts, don't
* reset the FIFOs are the user may want the data that's present
*/
XUartLite_WriteReg(dev->regs, XUL_CONTROL_REG_OFFSET, 0);
}
我们需要借鉴XUartLite_CfgInitialize函数,由于我们并未使用BSP提供的结构体,所以,这个函数中的填充结构体的操作就不用移植,只移植了writereg。
再看start。
void uart_lite_start(struct UartLiteDev* dev)
{
XUartLite_WriteReg(dev->regs, XUL_CONTROL_REG_OFFSET,
XUL_CR_ENABLE_INTR | XUL_CR_FIFO_RX_RESET | XUL_CR_FIFO_TX_RESET);
}
这里需要借鉴XUartLite_ResetFifos函数和XUartLite_Recv函数。
主要是设置reg,开中断,复位FIFO。
再看send。
void uart_lite_send(struct UartLiteDev* dev, u8* buf, int len)
{
for (int i = 0; i < len; i++)
{
XUartLite_SendByte(dev->regs, (u8)buf[i]);
}
}
借鉴于low_level中的例子,利用XUartLite_SendByte来传输。这个函数是一个自旋等待的函数,首先会判断是否能发送,如果不能,则自旋等待,可以发送则将byte发送出去。
同样的,XUartLite_RecvByte函数也是个自旋等待的函数,首先会判断接收FIFO是否为空,如果是空,则自旋等待,如果FIFO不为空,则有数据可以取出。
正是由于接收函数的自旋等待特性,所以不能在主线程中使用,否则只要没有数据,那么主线程将被卡死。所以接收必须使用中断。
再看看receive。
设计了一个ISR,在整板处理时,挂载到XINTC上。
void uart_lite_isr_handle(struct UartLiteDev* dev)
{
u8 retRegister;
u8 retRegisterVal;
u32 regs = dev->regs;
struct RingBuffer* recv_buff = &dev->recv_buff;
dev->irq_count++;
/*
* Enter a critical region by disabling all the UART interrupts to allow
* this call to stop a previous operation that may be interrupt driven
*/
retRegisterVal = XUartLite_GetStatusReg(regs);
XUartLite_WriteReg(regs, XUL_CONTROL_REG_OFFSET, 0);
/*
* Loop until there is not more data buffered by the UART or the
* specified number of bytes is received
*/
while (1) {
/*
* Read the ret Register to determine if there is any data in
* the receiver/FIFO
*/
retRegister = XUartLite_GetStatusReg(regs);
/*
* If there is data ready to be removed, then put the next byte
* received into the specified buffer and update the stats to
* reflect any receive errors for the byte
*/
/*
* There's no more data buffered, so exit such that this
* function does not block waiting for data
*/
if (retRegister & XUL_SR_RX_FIFO_VALID_DATA) {
ring_buffer_push(recv_buff, XUartLite_ReadReg(regs, XUL_RX_FIFO_OFFSET));
}
else {
break;
}
}
/*
* Restore the interrupt enable register to it's previous value such
* that the critical region is exited
*/
retRegisterVal &= XUL_CR_ENABLE_INTR;
XUartLite_WriteReg(regs, XUL_CONTROL_REG_OFFSET, retRegisterVal);
}
这里借鉴了XUartLite_ReceiveBuffer函数。
先保存寄存器,
然后关中断,
进入临界区,进行业务处理,
处理完后,再恢复寄存器。
处理部分,借鉴了XUartLite_ReceiveBuffer函数。循环接收,知道FIFO为空。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
串口接收到的数据,通常要经过CRC校验,
这里,设计了一系列CRC计算函数,用来对串口数据进行CRC校验。
u8 calc_sum_crc(u8* src, int len)
{
u8 sum = 0;
for (int i = 0; i < len; i++)
{
sum += src[i];
}
return sum;
}
int check_sum_crc(u8* src, int len, u8 crc)
{
if (calc_sum_crc(src, len) == crc)
return 0;
return -1;
}
u8 calc_xor_crc(u8* src, int len)
{
u8 sum = 0;
for (int i = 0; i < len; i++)
{
sum ^= src[i];
}
return sum;
}
int check_xor_crc(u8* src, int len, u8 crc)
{
if (calc_xor_crc(src, len) == crc)
return 0;
return -1;
}
u16 calc_crc16(u8 *src, int len)
{
int nleft = len;
u16 *w = (u16*)src;
u16 answer;
int sum = 0;
u16 tmp;
while (nleft > 1)
{
tmp=*w++;
sum +=tmp;
//sum += *w;
//w++;
nleft -= 2;
}
// mop up an odd byte, if necessary
if (nleft == 1)
{
sum += *(u8 *)w;
}
//add back carry outs from top 16 bits to low 16 bits
sum = (sum >> 16) + (sum & 0xffff); //add hi 16 to low 16
sum += (sum >> 16); //add carry
answer = ~sum; //truncate to 16 bits
return (answer);
}
int check_crc16(u8 *src, int len, u16 crc)
{
if (crc == calc_crc16(src, len))
return 0;
return -1;
}
累和校验,是将一个数组中的数据,逐个与中间结果相加,最终获得最终结果,即累和值。中间结果的初始值为0.
累异或校验,是将一个数组中的数据,逐个与中间结果异或,最终获得最终结果,即累异或值。中间结果的初始值为0.
CRC16校验,是一种更复杂的累和运算。