[PIC®/AVR®/dsPIC®产品]

【CuriosityNano测评报告】+PIC18F16Q41 :多中断搭多档方波发生器

[复制链接]
57604|13
手机看帖
扫描二维码
随时随地手机跟帖
nickwolfe|  楼主 | 2021-9-15 08:28 | 显示全部楼层 |阅读模式
本帖最后由 nickwolfe 于 2021-9-16 13:44 编辑

#申请原创# #申请开发板#
延续上一次的开箱体验,
今天我们要体验PIC单片机的各种基本中断,
还要学习使用Data Visualizer插件监测串口输出情况

组合串口中断、外部中断、定时器中断的功能,
我们搭建一个可以切换不同频率的方波发生器作为示范。


我们新建一个项目。如图所示,设置MCC

6967961413b39568f8.png
和上次一样,RC1设置为输出管脚
主频4MHz,内部时钟


1229561413b5bc5f34.png
Pin Module的tag当中,给RC1命名为LED,不用的Analog选项勾掉


6670861413b79c6fe0.png
Available Resources的tag当中,添加UART,我们准备使用串口

249861413b8dd7394.png
UART的tag里,点加号添加UART1,并再生成代码的下拉选项中、将“Basic Read/Write with Printf support”选上
我们后面会用printf来输出调试信息给串口,作为debug用。

882361413ba12369e.png
此时我们看到右边的Pin Manager: Package View的管脚图里,只有RB5|RX1
我们需要手动追加TX1到RB7,RB5/RB7是我们使用的这块C-Nano开发板硬件定义的

我们可以在Pin Manager: Grid View的tag里,点击TX1这一行对应的Port B的7管脚位置,
绿色小锁头标志表示分配没冲突


9322461413bba1e66d.png
点击左上方Project Resouce里面的UART1,我们开始设置串口参数
允许UART中断、并把STDIO指向串口输出。
这样我们的printf就可以与串口连通了。


9768261413bccdd1bc.png
在Interrupt Module的tag里,先打开上方的允许中断向量表,并确认UART1的中断正确设置完成
然后就可以点击左上方的Generate来为刚才选定的系统资源,生成MCC自动代码了


2301361413bf217a5e.png

MCC代码正常生成之后,我们打开main.c主程序
先勾掉全局中断允许这一行前面自带的注释符
然后我们测试一下刚才定义的LED管脚是否能间隔1.5秒闪烁
printf函数把LED当前的状态输出给串口


2283861413c083ad77.png

我们刚才打开了串口的接收中断,所以也要对串口接收到数据做一个处理
uart1.c里面的UART——RxDataHandler里,我们把接到的数据直接扔回给串口
向我们发信的人将在他的终端上看到同样内容的回文。


2187761413c1f64e9f.png

我们编译并烧写现有的程序到开发板上
看到烧录成功标志,我们开始考察开发板运行情况。



7836661413c3f099dc.png

开箱测试的时候,我们使用了传统的串口精灵程序来接收并监视开发板上串口输出的情况
其实MPLAB X IDE现在提供一个DV插件,这个Data Visualizer功能既有插件版、也有单独工作的standalone版
我们点击MCC旁边绿色的DV标志,打开Data Visualizer控制板
此时我们的开发板上虚拟串口,在这台PC上是COM6,所以我们点击COM 6后面的向右小三角,开始监控COM 6的数据流。
DV控制板的下方这个方框,提供了Terminal解码功能,
我们在其右侧点击下拉数据源,选择"COM 6 on nEDBG...."(各台电脑显示可能不同)
Terminal窗口就可以出现1.5秒轮流显示的LED is on/LED is off信息了。
和Terminal里面显示的内容同时,开发板上的LED也会一亮一灭。
这是我们在main.c里写下的代码,工作正常。


最后我们再在Terminal下方的"Line input"输入框里随意输入字符,模拟别人通过串口发给开发板的信息
测试开发板接收到外部串口信息的响应。
我们看到我们输入的"this is a test"正常被拾取、并通过串口发回。
到目前为止的开发都正常。我们建立了串口中断的输入输出,并予以测试。


4389561413c55c7ca4.png

接下来我们导入外部中断。
在Available Resources里添加 EXT_INT
在Interrupt Module的tag里、我们去掉INT1、INT2的对勾,今天我们不用它们
只留下INT0,对应的是RC0管脚。


2164661413c6a9757b.png

Pin Module里记得要把RC0对应的EXT_INT这一行里面的弱上拉WPU勾选上。
这样,RC0没有按键按下时管脚的电平为高、按下时为低。
设定好后我们再次点击左上角的Generate来生成EXT_INT相关的MCC代码

这次我们需要在两个地方增加外部中断的相关程序代码首先打开ext_int.c(在“源代码--mcc生成的代码”里)

我们在include之后、定义函数之前,添加一个标记发生了外部中断的变量:

uint8_t int_INT0_** = 0;

在void INT0_CallBack(void)函数中,
判断发生了INT0中断后(  if(INT0_InterruptHandler)  里面)
增加一行:
   int_INT0_** = 1;

保存ext_int.c

接着我们在main.c里面也添加对应EXT_INT的逻辑

在include后面添加int_INT0_**的外部变量声明

extern uint8_t int_INT0_**;

然后在while(1)里面追加下面的代码:
        if (int_INT0_** == 1) {
            printf("button pressed\r\n");
            int_INT0_** = 0;
        }


当主程序里发现有INT0中断发生时,向串口输出字符串,并清除中断标志,预备下一次服务。

我们编译并烧写,在开发板上确认运行效果。


4894061413c8391c5c.png

我们在Data Visualizer面板中看到,当开发板上SW按键按下时,Terminal里的确出现了"button pressed"字样
而此前写的代码都正常工作,不受新增的这个外部中断功能的影响。


7020661413c990497b.png

下面我们要增加一个新的中断:定时器中断。
我们在Available Resources的tag里添加TMR1,TMR1是个16bit的定时器,可以操作的范围比较大。



3401761413cbf035eb.png

如图所示设置定时器的参数,我们先设置一个500ms的定时中断,得到一个配置值0x85EE
时钟源采用FOSC/4,预分频也是1:4,并打开定时器的中断允许
最后我们点击Generate来生成MCC代码



8216761413cd3ae4c6.png

这次我们将使用这个500ms的中断来控制LED的亮灭,所以先在main.c中把刚才写的1.5一次的LED亮灭控制逻辑注释掉



933861413ce76ec99.png

新的LED亮灭控制,在tmr1.c里面,也是在“源文件--MCC生成的文件”下
我们找到程序的最后,void TMR1_DefaultInterruptHandler(void)函数里
我们追加  LED_Toggle();   这一行。

TMR1中断,根据我们在TMR1 tag里定义的时间(500ms),每500ms触发一次
定时器中断触发的时候,就会执行上面这个函数。

我们修改好程序,就可以编译并烧写到开发板看运行效果了



9926761413cfe3c1c5.png

这次我们在Data Visualizer面板的Terminal中看到LED is on / off的字样不见了(mian.c中已经注释掉)
只留下了按下SW按键时的输出响应("button pressed")
当然,Line input的输入仍旧功能正常,不受我们把mian里面控制LED亮灭的逻辑改到定时器中断处理中去的影响。
开发板上的LED,则比此前1.5秒一闪快了许多,现在是0.5秒一闪啦。



2471561413d14310c6.png

接下来我们要写个有意思的功能。
刚才的闪灯,我们利用TMR1的定时中断切换亮灭,点亮和熄灭的时间都约为500毫秒(ms)。
现在我们打算让点亮时间为10ms、熄灭时间90ms。
这样,一个亮灭周期共100ms,也就是说一秒钟要亮灭十次。
这也就是10Hz的方波信号,占空比为1:9。

定时器本身没有动态切换每次定时时长的功能,我们来发挥自己的智慧
首先我们打开TMR1 这个tag, 把中断定时设为90ms。手动记录下90ms的配置值为0xEA07
然后把时间改回到10ms,并点击Generate来生成新的MCC代码。



9629861413d2744478.png

因为刚才我们已经对tmr1.c进行过编辑、还追加了一些自己的代码。
MCC发现它生成的新代码与我们编辑好的tmr1.c有冲突,所以它弹出来一个merge合并对比窗口
每一处不同,它都询问咱们的意思,看是要它生成的、还是保留咱们刚才写好的。

比如TMR1的中断定时配置值,这个就得用他新生成的,因为原来那个版本咱们用的是500ms,新的值是10ms
所以我们在这两个配置值的冲突上,点击向右小箭头,用MCC生成的新数据来取代上个版本的tmr1.c

其他我们自己写进去的代码,在merge合并对比窗口呈现绿颜色,表示是我们这个版本有、MCC新生成的版本没有的
我们都选择保留我们自己的。这样得到一个合并后的新版本tmr1.c
今后随着我们对代码的修改增加,出现merge合并对比、冲突的情况,都会很多。
这需要我们清楚地了解自己对代码的修改都有哪些,才不至于搞混了


7065561413d3b8b0fd.png

除了MCC给我们新生成的代码,我们还需要客户化一下tmr1.c

通过研究原有的tmr1.c,我们发现timer1ReloadVal这个变量存放着TMR1的定时时长。
每次中断触发过后,中断处理程序会重新把这个值刷回去,预备下一次定时器中断。
那我们就仿效它,也做一个timer1ReloadVal2,来存放我们的第二定时时长。

另外,intTMR**是我们自己添加的一个标志,
在第一定时时长(原有的)和第二定时时长(我们新加的)之前切换时使用它作为区分。


6913561413d5a2d022.png

在这个实验里,我们的第二定时时长是个固定值,就是step_20时我们利用TMR1 tag里的计算器算出来的
所以在定时器1的初始化部分,直接写到我们新追加的这个timer1ReloadVal2里去,即可。



1694261413d7dee85b.png

tmr1.c的中断处理里,原本只有一行。
我们根据在0、1之间来回变动的intTMR**变量的值,决定到底把哪个定时时长写给定时器1

并且在每次触发了定时器中断的时候,我们切换intTMR**的值。原来是0就改为1,原来是1就变回0

编译并烧写,我们看到LED灯果然一秒闪动十次了!


7738761413d8f93203.png

我们继续扩写我们的定时器1,这次我们要预设六个频率的方波给定时器1,然后用按键来轮流切换频率。

首先我们追加一个专门输出方波波形的管脚,RC2,我们命名为PULSE。
而此前一秒闪了十次的LED,则改回去用main.c的while(1)控制。
看看我们的定时器中断、与主程序的亮灯控制,是不是能独立进行互不影响。

在MCC里添加好PULSE输出管脚的定义后,记得Generate来生成相应资源的MCC代码。



6130261413da2afcec.png

我们在main.c里恢复刚才被注释掉的LED控制代码。
同时,在tmr1.c最后那个函数里的LED_Toggle(); 要改为 PULSE_Toggle();

我们在main.c里,另外追加了一个变量int_FreqIndex,这个变量将在0到5间变动,对应未来的六种方波的频率。
按键外部中断发生时,该变量会加一、用来指向下一个频率,5之后再按键则回到0.



5579961413db5362ef.png

这个新变量对应的频率的名称,写在一个数组里了
按键后、通过printf函数将把对应的频率信息输出到串口。

完工后我们编译烧写,看看开发板的运行效果。



9720661413dc6b22f8.png

在Data Visualizer里,我们看到久违的LED is on / off回来了,
按键按下时、每次也都切换到下一个频率: 10Hz -> 20Hz -> 30Hz -> 40Hz -> 50Hz -> 100Hz -> 10Hz
循环往复。
当然,这只是按键的功能,正常的显示测试。真正的方波频率控制我们下一步着手去做。



9775761413dd789736.png

我们做一张表格,计算产生10Hz到100Hz的这六种频率不同占空比方波,我们需要的TMR1配置值、分别是多少

10Hz,周期就是100ms、也就是100000us。
如果点亮部分(高电平)固定为5ms=5000us,则周期里剩下的空的部分就是95ms=95000us
我们通过TMR的tag里的计算器,算出配置值应该为0xE8CE........

以此类推,我们的到整张表格所需要的数值(最后一列)。
我们可以开始写新一版的tmr1.c了!


6408561413dec56da6.png

timer1ReloadVal2变成了一个数组。内容就是我们上一步算出来的表格最后一列
我们声明int_FreqIndex为外部变量,
这样、在主程序main.c里控制着按键后显示哪个频率值的这个变量,也可以同时控制我们定时器定时时长的变化。



944596142d8cece9fc.png
看明白了吧?根据int_FreqIndex的不同,在轮到“空”的时候,也会有不同的定时中断时长写进去。



429436142d8e0f1822.png

编译并烧写进开发板
我们看到LED还是1.5秒亮灭一次
PULSE端则随着按键循环产生出不同的频率的方波。

734546142d90a952ac.png
10Hz

172176142d9255ffe3.png
20Hz

509446142d9365e663.png
30Hz

601496142d943c3f10.png
40Hz


770166142d9512b4cd.png
50Hz

150776142d9696b3c0.png
100Hz

产生的方波波形,在示波器上得以确认。


总结:
我们今天在Curiosity NANO这块开发板上操练了三种不同的中断,
分别是串口中断、外部中断、定时器中断TMR1

这些中断与main.c的while(1)循环是彼此独立运行的关系。
我们也可以在mian中写入中断的处理程序。
中断和主程序之间需要自己定义一些标记变量沟通。

我们也学会了使用DV插件来在特定的场景代替串口助手来监控开发板的串口输出









使用特权

评论回复
nickwolfe|  楼主 | 2021-9-15 08:28 | 显示全部楼层
啊啊啊,权限不足,图片没法发......等明天继续贴图交作业!

使用特权

评论回复
pzsh| | 2021-9-16 10:57 | 显示全部楼层
图文并茂, 好细致的步骤

使用特权

评论回复
pixhw| | 2021-10-2 12:49 | 显示全部楼层
使用了DAC了吗     

使用特权

评论回复
sdlls| | 2021-10-2 12:50 | 显示全部楼层
最大支持多大频率呢      

使用特权

评论回复
febgxu| | 2021-10-2 12:50 | 显示全部楼层
还需要多个中断呢         

使用特权

评论回复
xiaoyaodz| | 2021-10-2 12:50 | 显示全部楼层
测试的性能怎么样   

使用特权

评论回复
fentianyou| | 2021-10-2 12:50 | 显示全部楼层
Data Visualizer插件是什么呢     

使用特权

评论回复
selongli| | 2021-10-2 12:51 | 显示全部楼层
不同频率的方波发生器?

使用特权

评论回复
minzisc| | 2021-10-2 12:51 | 显示全部楼层
PIC18F16Q41价格怎么样   

使用特权

评论回复
lzmm| | 2021-10-2 12:51 | 显示全部楼层
可以做fft吗         

使用特权

评论回复
hudi008| | 2021-10-2 12:52 | 显示全部楼层
体验PIC单片机吧   

使用特权

评论回复
kkzz| | 2021-10-2 12:52 | 显示全部楼层
为什么不使用pic32呢?      

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

26

帖子

0

粉丝