本帖最后由 Fillmore 于 2019-3-14 16:50 编辑
RT Nano V3初级教程第二章 Finsh Shell.pdf
(785.16 KB, 下载次数: 13)
第二章 Finsh Shell在搞Finsh Shell之前先要搞定一个串口通讯,至少要保证能收数据能发数据。
2.1 串口由于使用的是LPC1768,直接参考一下BSP下的LPC176x工程的写法,之前我在网上看教程,都是从STM32讲起的,更可笑的是,我在做串口程序的时候,是从STM32的串口程序改过来的。。。好浪费时间呀。。。 当时参考 “RT thread 设备驱动组件之USART设备”这个文档做的,前人种树,后人乘凉呀。现在知道学习方法了没有?先去看一下BSP里面有没有对应的例程序,有的话,直接参考。不懂了再看网上相关的文章,之前俺的学习路线出了点差错,不过还好,跑起来了。
现在,我们在前一节的基础上,添加UART。
先来看一下uart.c这个文件里面都有什么鬼,定义了一个结构体,几个函数,如图2-1所示。
图2-1 uart.c结构图 注意了,以前总是说static定义的函数只能内部使用,外部不能用呢??什么鬼??实际上是错误的,之前也做过实验,通过指针的方法是可以访问的,可能应当改写成“static定义的函数,不能直接被外部的文件显式调用,指针方法可以调用”。
串口0各函数的作用如表2-1所示,其中rt_hw_uart_init函数是供外部调用的初始化函数,它主要任务是完成设备接口函数的绑定,并将设备注册,如图2-2所示。
表2-1 串口0各函数的作用 [mw_shl_code=c,true]UART0_IRQHandler 串口0的中断函数,接收数据到缓冲区
rt_uart_init 串口0的初始化函数
rt_uart_open 串口0的打开函数,开中断
rt_uart_close 串口0的关闭函数,关中断
rt_uart_read 串口0的读取函数,从缓冲区读取数据
rt_uart_write 串口0的发送函数
rt_hw_uart_init 将以上函数与设备结构体绑定,并注册[/mw_shl_code]
图2-2 串口初始化函数 首先定义了一个结构体指针uart,这个指针指向实体uart_device,然后对实体进行赋值,其中那个parent就是rt_device,这个取的名字也很讨厌,为什么不直接写成device这样的名字呢?可以自己改的,嘿嘿,我也懒得去改,知道是它就成。最后一句,注册到RTT。注意了,这里只是小学生刚来注册报名登记而已,什么事情都还没有干,相当于还没有安排座位(真正的初始化),还无法开始听课(数据读取和写入操作)。
再说一下那个结构体是怎么回事吧。 [mw_shl_code=c,true]struct rt_uart_lpc
{
struct rt_device parent;
/* buffer for reception */
rt_uint8_t read_index, save_index;
rt_uint8_t rx_buffer[RT_UART_RX_BUFFER_SIZE];
} uart_device;[/mw_shl_code]
rt_device这个是系统定义好的结构体,用户需要自定义设备结构体时,都从这个地方去扩展,把它放到最前面是为了方面取地址和强制内型转换使用(强制转换会不会把数给弄坏了?以前我也这样想。我们换一个思路来看这个问题,那个东东原原本本在那里,我们用一个相机拍个照(读取数据),照片是有参考坐标的,不同的坐标读取不同的值,在上面画上网格,格子大小不同,读取的数据就会不同,但是原有的数据不会变,除非你写入。)
2.2 添加串口文件
在工程目录下建立一个文件夹Drivers,并将LPC176x下的uart.c和uart.h复制过来,然后在工程添加Drivers目录,将uart.c添加进去。如图2-3中的1所示。然后添加头文件的目录,如图2-3中的2和3操作,将头文件目录包含进来,如图2-4所示。
注意一下,44行,这里对两个宏进行了判断,只有打开了这两个开关,下面的代码才参考编译呢。
![]() 5.jpg(7.83 KB, 下载次数: 5)
图2-5编译条件
2.3 打开串口支持
查找一下那两个宏定义在什么地方,悲剧的是,RT_USING_UART0个宏没有定义的地方,另一个宏在rt_config.h里面有,但是长相不好看呢。先看一新旧配置文件的对比图吧,如图2-6、2-7所示。
![]()
图2-6 新版配置文件 没有那个Device选择的地方了,怎么办,看一下里面都写了什么,如图2-7所示。RTE_USING_DEVICE也没有找到定义呢,要用怎么办?手动添加吧。 ![]()
![]()
图2-7 配置文件信息 编译,出错了哦,NND,这个Nano 3改进得怎么这么不人性化呢?看一下是什么?说是没有定义 Drivers\uart.c(56): error: #20: identifier "RT_UART_RX_BUFFER_SIZE" is undefined 怎么办?继续添加,参考一下以前的代码,定义是这个样子的: #define RT_UART_RX_BUFFER_SIZE (RT_CONSOLEBUF_SIZE >> 1) 至为什么呢?我也不知道,没有看文档,用PP想一下,那个定义之后有64个字节,从键盘输入超过64个字节的将会是什么指令呢?哥都不让它这么多输入,浪费老子空间。最后配置文件如图2-8所示。 ![]()
图2-8 手动修改配文件
再编译,没有错误了。接下来要添加Finsh Shell了。
2.4 Finsh Shell支持OMG,算了放张图就好了,如图2-9所示。(这里实在是太简单了,无法用语言表达,请看图。)
11.jpg(112.08 KB, 下载次数: 5)
图2-9添加Finsh Shell
编译,没有错误哦。
2.5 检查一下查找一下初始化函数,如图2-10所示。坑货,怎么没有调用呀?
注意,编译没有错误,并不代码程序执行没有错误,不然怎么会出现BUG呢?编译成功只能说明,语法没有问题,符合编译器的逻辑。
2.6 Shell使用uart0输出口选择设备“uart0”,注意了,这里的uart0不是指LPC上的串口0,指的是名字为uart0的那个设备。所以,不要以为只能是串口哦,别的接口,可能也是可以用的,当然,我没有试过,机会留给大家。 添加到board.c中去,如图2-12所示。 使用PuTTY查看运行结果怪了,怎么这样也可以运行?记得之前在2.1.1时,如果没有进行Linker的相关设置时,是不能运行的,这个RT_thread进行改进了?反正我是不清楚啦,我也刚玩不久。但是屏上显示乱码呢,怎么回事?是不是和那个Linker设置有关呢?啥子情况呢?而且每次启动都不一样哦。先展示一下启动时的图片,如图2-14所示。
2.9 试着添加Linker设置
如图2-15所示。添加 --keep*.o(.rti_fn.*) --keep *.o(FSymTab)
编译运行,乱码还在。说明不是这个问题,位置都不是固定的,那就肯定是波特率不准造成的啦,修改一下试试。
2.10 修改波特率偿试"
结果是,好了!想骂人不?哪个瓜娃子写的底层驱动呀?BSP不准哦!!!修改这个是治标不治本的法子哦!
2.11 为什么会有乱码说实话,今天我也第一次使用Rt_thread中提供的uart驱动,没有想到它会有这个问题。以前在做项目时,我遇到过几次,最后都归结于使用现成驱动代码造成的后果,波特率不准。来,看一下它的初始化长什么样,如图2-17。
为什么会不准呢?两种波特率上都会有误差,为什么115200bps时就显示出来了呢?上面的除法表示什么?1bit位要跑几个时钟嘛,对比一下就知道了,哪个相对误差大,误差大的肯定会出错,当然,当其字符数最在很少的情况是不容易显现出来。有一种误差叫累计误差,误差只有累计到一定程度时,才表现出来。简单地理解为时钟不一样通信时,前肯定没有问题,但是累计到一定程序时,出现大偏差,周而复始。
2.12 修正函数. b. t. A9 U- z/ U- I) n
基实NXP官方给出了波特率修正的文档,可以肯定的说,写那个驱动的小哥,一定没有看过那个文档,或是看过了,没有在意。这里为大家提供一个函数,直接使用就可以了。
[mw_shl_code=c,true]//add by hjb 精准波特率
voidrt_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;
}
}
}
//[/mw_shl_code]
|