引言
作为郑州的嵌入式新人,最近接到两个任务,第一个是根据门禁设备的驱动程序写出一个接受串口数据的应用,这个串口用于连接读卡器等设备。第二个是把libpng移植到门禁设备上,由于我们的门禁设备只支持jpg格式的图片,因此要用这个库把png图片转为raw格式,然后再转换为jpg格式。第一个任务以及完成,今天先整理第一个任务。
开发过程及源码
首先在主程序中重新开一个串口任务
#if defined(USING_UART_Receive_ENABLE)
#define UART_RECEIVE_PRI 31
printf("uart_receive test.\n");
aos_task_t uart_receive_thread;
ret = aos_task_new_ext(&uart_receive_thread, "uart_receive", uart_reader_task, NULL, 10*1024, UART_RECEIVE_PRI);
#endif
在uart_reader_task函数中进行串口的初始化,配置等操作,首先进行引脚复用,然后使能时钟,开启相应引脚功能,由于是多线程任务,为了保证数据的完成行,创建信号量进行处理。在本任务中主要需要关注的是csi_uart_initialize(),csi_uart_config(),csi_uart_receive()。
void uart_reader_task(void *paras)
{
uint8_t *p;
int i=0;
int ret=-1;
int rece_size =0;
int cnt;
device_event_t * evt;
drv_pinmux_config(OTP_MUX,UART1_MUX);
drv_disable_uart_clock(1);
drv_uart_clk_sel(1, CLK_SEL_PLL);
drv_enable_uart_clock(1);
drv_set_uart_freq(1,10,275);
ret = krhino_sem_create(&uart_reader_sem, "UART-READER-SEM", 0);
if (ret != RHINO_SUCCESS) {
return ret;
}
handle = csi_uart_initialize(1, uart_receive_event_cb);
ret = csi_uart_config(handle, 9600,UART_PARITY_NONE,UART_STOP_BITS_1, UART_DATA_BITS_8);
csi_uart_receive(handle,NULL,0);
while(1)
{
ret = krhino_sem_take(&uart_reader_sem, RHINO_WAIT_FOREVER);
if (ret != RHINO_SUCCESS) {
k_err_proc(ret);
}
if(uart_reader_state == UART_READER_STATE_FULL)
{
evt = device_event_construct("open",DeviceEventTypeOperation);
if(evt)
{
evt->data =device_event_alloc(evt,16);
p = evt->data;
*p = 0x26;
p++;
*p = uart_reader_frame[9];
p++;
*p = uart_reader_frame[8];
p++;
*p = uart_reader_frame[7];
device_event_notify(evt);
}
uart_reader_state = UART_READER_STATE_INIT;
}
}
// ret = csi_uart_uninitialize(handle);
}
其中有意思的是在初始化配置的时候还能这样定义结构体数组,让我惊呆了(菜鸟新人看的代码少,没见过世面)这样的写法让代码看着更加整洁美观。到这一步串口已经配置好,相应的中断已经打开,众所周知,串口的接收主要靠中断完成,由于刚入行先从嵌入式应用做起,驱动我就不那么细看了,主要关心厂家给出的接口。
struct {
uint32_t base;
uint32_t irq;
void *handler;
}
static const sg_uart_config[CONFIG_UART_NUM] = {
{CSKY_UART0_BASE, UART0_IRQn, UART0_IRQHandler},
{CSKY_UART1_BASE, UART1_IRQn, UART1_IRQHandler},
{CSKY_UART2_BASE, UART_CK804_IRQn, UART_CK804_IRQHandler},
{CSKY_UART3_BASE, UART_CK805_IRQn, UART_CK805_IRQHandler},
};
int32_t target_uart_init(int32_t idx, uint32_t *base, uint32_t *irq, void **handler)
{
if (idx >= CONFIG_UART_NUM) {
return -1;
}
if (base != NULL) {
*base = sg_uart_config[idx].base;
}
if (irq != NULL) {
*irq = sg_uart_config[idx].irq;
}
if (handler != NULL) {
*handler = sg_uart_config[idx].handler;
}
return idx;
}
在中断函数中进入相应的事件,由于是接收,进入 ck_uart_intr_recv_data(uart_priv);这个函数
void ck_uart_irqhandler(int idx)
{
ck_uart_priv_t *uart_priv = &uart_instance[idx];
ck_uart_reg_t *addr = (ck_uart_reg_t *)(uart_priv->base);
uint32_t intr_state = addr->UART_FCR_IIR & 0xf;
if (intr_state & IIR_INT_TX_EMPTY) {
ck_uart_intr_send_empty(uart_priv);
} else if (intr_state & IIR_INT_RX_THOLD) {
ck_uart_intr_recv_data(uart_priv);
} else if (intr_state & IIR_INT_UART_STOP) {
ck_uart_intr_char_timeout(uart_priv); //receive small data
} else if (intr_state & IIR_INT_UART_PERR) {
ck_uart_intr_process_error(uart_priv);
}
}
在初始化串口的时候已经把回调函数注册进去了,在串口进入相应事件的时候会调用回调函数,我们根据状态在回调函数中对串口进行处理。在本项目中主要应用了UART_EVENT_RECEIVED这个事件,csi_uart_receive_query这个函数是从FIFO(First Input First Output的缩写,先入先出队列中)读取数据
static void uart_receive_event_cb(int32_t idx, uint32_t event)
{
int rx_count;
switch (event) {
case UART_EVENT_SEND_COMPLETE:
// printf("send complete!!!!!!\r\n");
break;
case UART_EVENT_RX_TIMEOUT:
uart_reader_state = UART_READER_STATE_INIT;
break;
case UART_EVENT_RECEIVE_COMPLETE:
rx_count = drv_usi_usart_get_rx_count(handle);
// printf("rx_count %d\n",rx_count);
break;
case UART_EVENT_RECEIVED:
if(uart_reader_state != UART_READER_STATE_FULL)
{
rx_count = csi_uart_receive_query(handle,uart_reader_buf,sizeof(uart_reader_buf));
// printf("rx_count %d\n",rx_count);
if(rx_count>0)
uart_reader_read_frame(uart_reader_buf,rx_count);
}
break;
default:
break;
}
读取数据后利用uart_reader_read_frame按照通信协议进行拼包,在拼包过程中一定要考虑全面,把各种边界条件考虑进去,这里我把独创源码进行分享了,如果感觉有用的话记得点赞评论哦~
static int uart_reader_read_frame(uint8_t * data,int rx_count)
{
int i=0;
while(i<rx_count)
{
if(uart_reader_state == UART_READER_STATE_INIT)
{
if(data[i]!=0x20)
{
i++;
continue;
}
uart_reader_state = UART_READER_STATE_HEAD;
uart_reader_len = 0;
i++;
uart_reader_off = 0;
if((rx_count-i)<3)
{
if((rx_count-i)>0)
{
// printf("(rx_count-i):%diiiii\n",(rx_count-i));
memcpy(&uart_reader_frame[uart_reader_off],&data[i],(rx_count-i));
uart_reader_off += (rx_count-i);
}
return;
}
}else if(uart_reader_state == UART_READER_STATE_HEAD)
{
// printf("uart_reader_off:%d\n",uart_reader_off);
memcpy(&uart_reader_frame[uart_reader_off],&data[i],3-uart_reader_off);
uart_reader_len = uart_reader_frame[2];
// printf("uart_reader_len:%d\n",uart_reader_len);
uart_reader_len+=2;
uart_reader_state = UART_READER_STATE_DATA;
i+=(3-uart_reader_off);
uart_reader_off=3;
if((rx_count-i)<uart_reader_len)
{
// printf("(rx_count-i):%d\n",(rx_count-i));
if((rx_count-i)>0)
{
// printf("(rx_count-i):%dhhhh\n",(rx_count-i));
memcpy(&uart_reader_frame[uart_reader_off],&data[i],(rx_count-i));
uart_reader_off += (rx_count-i);
}
return ;
}
// printf("begain rev data\n");
}else if(uart_reader_state == UART_READER_STATE_DATA)
{
int j;
// printf("uart_reader_off:%d\n",uart_reader_off);
memcpy(&uart_reader_frame[uart_reader_off],&data[i],uart_reader_len+3-uart_reader_off);
i+=(uart_reader_len+3-uart_reader_off);
uart_reader_off = 0;
printf("receive:");
for(j=0; j<uart_reader_len+3; j++)
{
printf("%2.2x",uart_reader_frame[j]);
}
printf("\n");
j = krhino_sem_give(&uart_reader_sem);
if (j != RHINO_SUCCESS) {
return;
}
uart_reader_state = UART_READER_STATE_FULL;
}else{
printf("receive complete????????\r\n");
}
}
}
到这里一个完整的串口数据包以及从串口中获得,剩下的就可以提取想用的信息进行处理了。
牵扯到的小知识整理
小结
这个任务共做了4天,难度主要在于接口文档不全,以及ALIOS的资料较少,通过这个任务对自己的C语言提升很大,代码基本上都可以看懂了,特别是利用指针在程序中传递数据,指针在传递数据的时候可以这样理解,你向某个地址放东西,如果你知道这个地址,那么在哪个文件中都可以取这个数据,因为数据放的地址已经知道了,很灵活。就是把脑子的概念扭转过来,对习惯对地址操作的思维方式。接下来开始移植libpng,下篇文章再见!