从SOD到OOD(uartlite模块)

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校验,是一种更复杂的累和运算。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值