Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
NXP 微控制器
[新手试水] LPC1768_Nano3_UART
发布于 2018-03-25 17:15:58 浏览:3692
订阅该版
* 本帖最后由 wlof 于 2018-3-25 17:15 编辑 * **第五章 UART那点事**[align=center]Wlof摘要:本文主要介绍LPC1768上uart的使用,基于lpc176xbsp中的实现代码,针对其不适用于实际项目的地方进行了修改,希望对新手们有所帮助。 **5.1 起源**Uart相信是用得最多最广泛的东西,自本教程编制开始,最早用到uart的地方在Shell那节里面,由于之前只是为了快速上手,先把RTT玩起来,以便让新手有信心和动力继续向前,没有对uart进行解读,只是把别人实现好了的东西复制过来用而已。至于那个模板,在没有做修改的情况下,它仅适用于Shell,很难用到实际工程中去,特别是对于新手来说,都不知道把自己的代码加在什么地方合适。【**主要是不看文档造成的,我也不喜欢看文档,也是参考其他文章才知道的,哪个文章我忘记了,嘿嘿**】 【提示:本文件基于前一节的工程进行文件添加和修改,当然,所有操作都不受前节的限制,只是后面提供的工程文件包含了前节的内容,今后的所有工程均是如此。】 **5.2 Shell uart回顾**在前面的Shell使用中,我们提到了现有bsp包下的uart驱动存在一定的Bug,导致出现乱码的问题,为了修正这个错误我们添加一个修正函数,但用小数发生器,对波特率发生器进行修正,保证了正常的数据输出,修正函数如下: ```//add by hjb 精准波特率,【添加到bsp 176x uart.c文件中】 void rt_uart_precise_baudset(rt_uint32_t bps, rt_uint8_t *m_fdr, rt_uint32_t *m_fdiv) { typedef struct { rt_uint8_t Div: 4; //分频 rt_uint8_t Mul: 4; //倍数 } rt_uart_dcm_tbl; //1-[000-999]+\, rt_uart_dcm_tbl const tbl[] = { { 0, 1 }, { 1, 15}, { 1, 14}, { 1, 13}, { 1, 12}, { 1, 11}, { 1, 10}, { 1, 9 }, { 1, 8 }, { 2, 15}, { 1, 7 }, { 2, 13}, { 1, 6 }, { 2, 11}, { 1, 5 }, { 3, 14}, { 2, 9 }, { 3, 13}, { 1, 4 }, { 4, 15}, { 3, 11}, { 2, 7 }, { 3, 10}, { 4, 13}, { 1, 3 }, { 5, 14}, { 4, 11}, { 3, 8 }, { 5, 13}, { 2, 5 }, { 5, 12}, { 3, 7 }, { 4, 9 }, { 5, 11}, { 6, 13}, { 7, 15},//-----------4*9 { 1, 2 }, { 8, 15}, { 7, 13}, { 6, 11}, { 5, 9 }, { 4, 7 }, { 7, 12}, { 3, 5 }, { 8, 13}, { 5, 8 }, { 7, 11}, { 9, 14}, { 2, 3 }, { 9, 13}, { 7, 10}, { 5, 7 }, { 8, 11}, { 11, 15}, { 3, 4 }, { 10, 13}, { 7, 9 }, { 11, 14}, { 4, 5 }, { 9, 11}, { 5, 6 }, { 11, 13}, { 6, 7 }, { 13, 15}, { 7, 8 }, { 8, 9 }, { 9, 10}, { 10, 11}, { 11, 12}, { 12, 13}, { 13, 14}, { 14, 15}, }; rt_uint8_t i = 0, k = 0, j = 0; rt_uint8_t m_err[72] = {0}; rt_uint32_t fDiv, uDLest; rt_uint32_t uartClock = SystemCoreClock / 4; // 外设时钟 float fFRest = 1.5;// tFRest = 1.5, tAbs, min; if (uartClock % (16 * bps) == 0) // PCLK / (16*bps)为整数 { m_fdr[0] = 0x10;// 关分频器 m_fdiv[0] = (uartClock >> 4) / ( bps ); return; } k = 0xff; for(i = 0; i < 72; i++) //遍历 { uDLest = (uint32_t)(uartClock * tbl*.Mul / (16 * bps * (tbl*.Mul + tbl*.Div))); fFRest = (float)(uartClock * tbl*.Mul) / (float)(16 * bps * uDLest * (tbl*.Mul + tbl*.Div)); //频率相对偏差 fDiv = (uint32_t)((fFRest - 1) * 10000); if(fDiv > 0xff) { m_err* = 0xff; } else { m_err* = fDiv; } if(m_err* < k) { k = m_err*; //取误差最小那个 j = i; m_fdr[0] = tbl[j].Div | (tbl[j].Mul << 4); m_fdiv[0] = uDLest; } } } ``` 用法如下:【bsp 176xusrt.c中进行修改】 ``` rt_uart_precise_baudset(UART_BAUDRATE, &m_fd, &Fdiv); LPC_UART0->LCR = 0x83;/* 8 bits, no Parity, 1 Stop bit */ LPC_UART0->DLL = Fdiv % 256; /*baud rate */ LPC_UART0->FDR = m_fd; /*fix here */ LPC_UART0->DLM = Fdiv / 256; LPC_UART0->LCR = 0x03;/* DLAB = 0 */ LPC_UART0->FCR = 0x07;/* Enable and reset TX and RX FIFO. */ ```**5.3 哪里不适用了呢?**首先,俺要声明的是,bsp中的那个代码写得挺好的,也很好理解,但是呢,在实际使用的时候,我们的数据不可能只限制在255个字节之内,Shell可以这样弄。所以,要对结构体进行修改,将缓冲区弄大,对应的计数器也要加大。其次,只有一个缓冲区呢,不太好,读读写写都在这个地方,要是出错了怎么办呢,所以我们分别设立接收和发送缓冲区。第三,不知道在什么地方解析呢,数据接收了之后,怎么办呢? 现在,基于这3个问题,展开讨论一下,很快就结束了。 **5.4 添加新的结构体**前人种树,后人乘凉,这是常态,不要动不动就去砍树,破坏环境不说,万一搞出个灾难来就不好了。搞代码也是一样的,人家写好的东西,发布出来,肯定测试过了,在没有特殊情况时,可以不用修改。当然,我们现在所说的就是遇到3种特别情况了。【1】复件文件修改名字 将uart.c和uart.h复制一下,粘一下,重命名为uart_ex.c和uart_ex.h,如图5-1所示。[align=center]![1.png](/uploads/201803/25/170644ukb5kbspaet005a0.png)[align=center]图5-1 复制文件修改名字【2】添加到工程,修改包含文件打开uart_ex.c将包含文件修改为uart_ex.h,如图5-2所示。 [align=center]![2.png](/uploads/201803/25/170644xegweib4tj5954w9.png)[align=center]图5-2 包含修改 【3】修改函数名 为了以示区别,删除串口中断函数,将所有函数名都添加x表示扩展过的,如图5-3所示。初始化函数中的名字也要进行修改。 [align=center]![3.png](/uploads/201803/25/170644oql1pn0lbljba35t.png)[align=center]图5-3 函数名修改 【4】修改结构体 为了以示区别,将所有函数名都添加x表示扩展过的。 ```#define RT_UART_EX_BUFFER_SIZE 1024 struct rt_uart_ex_lpc { struct rt_device parent; /* buffer for reception */ rt_uint16_t read_index; //数据读取 rt_uint16_t save_index; //数据保存 rt_uint8_t recv_state; //数据接收成功标志 rt_uint8_t m_res; //数据接收成功标志保留 rt_uint16_t recv_length; rt_uint16_t dat_index; //数据序号【接收到的】 rt_uint16_t send_length; rt_uint8_t rx_buffer[RT_UART_EX_BUFFER_SIZE]; //接收缓冲 rt_uint8_t tx_buffer[RT_UART_EX_BUFFER_SIZE]; //发送缓冲 }; ``` **5.5 添加数据接收回调函数**在初始化时,添加一个数据接收函数,这个函数将用来接收数据,当数据满足协议时,设置一个标志位,当然更好的方法是,发送一个消息什么的,由于咱也是初玩过来的,先搞个标志先,后面再优化。staticrt_err_t rt_uartx_intput**(**rt_device_t dev**,** rt_size_t size**)****{** return 0;**}**初始化中加入以下代码:uart**->**parent**.**rx_indicate**=** rt_uartx_intput**;** //添加数据接收回调初始化变成:```void rt_hw_uartx_init(void) { struct rt_uart_ex_lpc *uart; /* get uart device */ uart = &uart2_device; /* device initialization */ uart->parent.type = RT_Device_Class_Char; rt_memset(uart->rx_buffer, 0, sizeof(uart->rx_buffer)); uart->read_index = uart->save_index = 0; /* device interface */ uart->parent.init = rt_uartx_init; uart->parent.open = rt_uartx_open; uart->parent.close = rt_uartx_close; uart->parent.read = rt_uartx_read; uart->parent.write = rt_uartx_write; uart->parent.control = RT_NULL; uart->parent.user_data = RT_NULL; uart->parent.rx_indicate= rt_uartx_intput; //添加数据接收回调 rt_device_register(&uart->parent, "uart2", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_INT_RX); if(RT_EOK == rt_device_init(&uart->parent)) //初始化时直接打开它省得忘记掉 { rt_device_open(&uart->parent, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX ); } } ``` **5.6 通信协议**假设通信协议为【FEFE6800】【ID】【CMD】【LEN】【DAT】【CS】【16】【FEFE6800】4个字节固定头,【ID】1个字节设备ID,【CMD】1个字节指令码【LEN】2个字节数据长度,低字节在前【DAT】数据域,长度由LEN指定【CS】【16】1字节数据校验码,累加和【16】1字节结束符 **5.7 接收函数回调** 1.为了找到数据头,直接判断连续字节是否存在FEFE6800,若有则认为是起始2.读取数据长度3.计算校验 这样处理之后,只要检测这个uart**->**recv_state是否为UART_RX_OK就成了。```static rt_err_t rt_uartx_intput(rt_device_t dev, rt_size_t size) { struct rt_uart_ex_lpc *uart = &uart2_device; //length = uart->save_index - uart->read_index; switch(uart->recv_state) { case UART_RX_NULL: //接收数据,用于头部判断 { if(size >= 4) { if((uart->rx_buffer[uart->save_index-1] == 0x00)&&(uart->rx_buffer[uart->save_index-2] == 0x68)) { if((uart->rx_buffer[uart->save_index-3] == 0xfe)&&(uart->rx_buffer[uart->save_index-4] == 0xfe)) { uart->rx_buffer[0] = 0xfe; uart->rx_buffer[1] = 0xfe; uart->rx_buffer[2] = 0x68; uart->rx_buffer[3] = 0x00; uart->save_index = 4; uart->read_index = 0; uart->recv_state = UART_RX_HEAD;//头部接收成功 } } } } break; case UART_RX_HEAD: //数据头接收成功 { if(size == 8) { uart->recv_length = uart->rx_buffer[6] | (uart->rx_buffer[7] << 8); if(uart->recv_length != 0) { uart->recv_state = UART_RX_BOD; } else { uart->recv_state = UART_RX_END; } uart->recv_length++; //cs加进来,接完也再判断 } } break; case UART_RX_ID: case UART_RX_CMD: case UART_RX_LEN: break; case UART_RX_BOD: { uart->recv_length--; if(uart->recv_length == 0) { uart->recv_state = UART_RX_END; } } break; case UART_RX_END: //把cs计算放到里后来,后面没有数据了,不怕,要不就把Cs存起来,每接一次加一次 { if((uart->rx_buffer == 0x16) &&(checksun(uart->rx_buffer,size))) { uart->recv_state = UART_RX_OK; } else { uart->recv_state = UART_RX_NULL; } } break; case UART_RX_OK: //等待程序进行检查读取 { __NOP(); } break; default: break; } return RT_EOK; } ``` **5.7 发送函数**```void rt_hw_uartx_senddat(void) { struct rt_uart_ex_lpc *uart; /* get uart device */ uart = &uart2_device; if((uart->parent.open_flag & RT_DEVICE_OFLAG_OPEN) != 0) { if(uart->send_length != 0) { rt_device_write(&uart->parent, 0, uart->tx_buffer, uart->send_length); } } } ``` **5.8 任务处理** if(rt_hw_uartx_rxok())//接收到数据,校验已过 { rt_hw_uartx_parse();//数据解析,填充数据发送区 rt_hw_uartx_senddat();//发送数据,清除发送长度(标识) } **5.9 总结** 这里主要是用到了回调函数,其实打开那个中断就可以看到,它有显明的调用哦![align=center]![4.png](/uploads/201803/25/170644dunak1nen40iv20e.png)[align=center]图5-4明显的回调[align=center] 文档下载:![RT_Nano_V3初级教程_5 UART那点事.pdf](/uploads/201803/25/170710qa8i8dqdyd86akyq.attach) 工程下载:![CRSytem_RTT3_UART2.7z](/uploads/201803/25/170706k9vc43aa8xo3rxav.attach)
查看更多
6
个回答
默认排序
按发布时间排序
tanek
2018-03-25
这家伙很懒,什么也没写!
欢迎提交pr,使得bsp更好用。你也可以在rtt的源码里面留下一个脚印。。。 nano包里面的源码就有我的名字,哈哈哈。
wlof
2018-03-27
这个家伙不懒,什么也没写
>欢迎提交pr,使得bsp更好用。你也可以在rtt的源码里面留下一个脚印。。。 > >nano包里面的源码就有我的名字, ... --- 申请了号,不会弄那个git,从来没有玩过...乱整了一通。;P 您给弄一个github的详细使用教程呗(图文并茂最好),管理员置项写的那个文档俺们这些新手们实在是看不懂呢。
tanek
2018-03-27
这家伙很懒,什么也没写!
>申请了号,不会弄那个git,从来没有玩过...乱整了一通。 >您给弄一个github的详细使用教程呗(图文并茂 ... --- 我看了你的github的commit。 你commit了两次, 这是修改不同的模块的,只提交了一次pr,这不符合pr的规范。 第一个commit我没环境测试,所以我只看了代码,代码格式有些问题,你可以用astyle来格式化一次的。还有一些注释掉的代码没有删除,这个建议删除。另外pr建议说明为什么,例如你这个是修复了有关波特率的问题,你可以在pr里面描述你为什么要这样做。 第二个commit我没理解你的意思,为什么要屏蔽这些功能键。
wlof
2018-03-27
这个家伙不懒,什么也没写
>我看了你的github的commit。 >你commit了两次, 这是修改不同的模块的,只提交了一次pr,这不符合pr的规范 ... --- 哦,是这样的,那个git不会玩,不知道怎么弄。 1、uart那个是修正波特率问题。 2、现有的shell存在密码输入不能删除的问题,同时方向键,那个几功能都能上码,这是不对的。 那个提交过去自动合到一个里面去了,不知道为什么,英文太次,看不懂那玩样,到底是干什么的,嘿嘿。
tanek
2018-03-28
这家伙很懒,什么也没写!
>哦,是这样的,那个git不会玩,不知道怎么弄。 >1、uart那个是修正波特率问题。 >2、现有的shell存在密码输 ... --- 嗯嗯, 我明白了。 git相关的我懂怎么用,但是教会别人使用还是不行。 大体意思是,你可以本地基于最新源码修改修复uart的问题,让推送到你的git的另外一个分支。 shell也是这样操作。 你可以搜索git的教程学习一下。
wlof
2018-03-28
这个家伙不懒,什么也没写
>嗯嗯, 我明白了。 >git相关的我懂怎么用,但是教会别人使用还是不行。 --- 嘿嘿,还是搞不明白,就那样吧。俺要赶紧把基础试水搞完,过几天玩一下别的操作系统FREE RTOS,在CUBEMX下弄一下,也是添加类似的功能,对比一下操作复杂度。:lol
撰写答案
登录
注册新账号
关注者
0
被浏览
3.7k
关于作者
wlof
这个家伙不懒,什么也没写
提问
24
回答
64
被采纳
0
关注TA
发私信
相关问题
1
试贴-消灭0主题
2
LPC M4的一些资料
3
LPC4088的临时分支
4
lpc1788 ad 不稳定
5
1788 LCD控制器缓冲区字节问题
6
一起来学习LPC4088吧
7
上传LPC4088的realtouch主工程
8
RealBoard 4088预定帖 [第一批板子不多,预定提前结束]
9
晒RealBoard LPC4088开箱照啦,速带小板凳前来围观
10
4088主程序需要的SD卡资源
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
CherryUSB的bootuf2配置
2
在用clangd开发RTT吗,快来试试如何简单获得清晰干净的工作区
3
GD32F450 片内 flash驱动适配
4
STM32H7R7运行CherryUSB
5
RT-Smart首次线下培训,锁定2024 RT-Thread开发者大会!
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
PWM
freemodbus
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
踩姑娘的小蘑菇
7
个答案
2
次被采纳
a1012112796
12
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
6
次点赞
lizimu
2
篇文章
7
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部