标题: PIC单片机输出不了占空比50%的方波——帮扶一把学单片机的60后 [打印本页]
作者: xxll 时间: 2021-11-24 11:18
标题: PIC单片机输出不了占空比50%的方波——帮扶一把学单片机的60后
自我介绍下:60后从业模电,自封工程师,动手能力强。学单片机历程30年,学级仍是未入门的学前班。所以各位看官都是我的老师,各位的每句话都能使我进步!期望大家都出一份正能量!废话不说入正题:
PIC12F683单片机,编程平台MPLAB IDE -V8.84 编译器HI-TECH-PICC_9.83
无单片机开发板,有RF809H编程器,有GA1202CAL示波器
实验要求单片机GP0和GP1输出20KHz占空比为50%的互补方波
C程序:
#include <pic.h>
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
GPIO=0X00;
}
void timer0_init(void) //定时器0初始化使用系统默认时钟4M fosc/4 所以计数周期为1us
{
T0CS=0; //timer0工作于定时器方式
PSA=1; //timer0不分频
T0IF=0; //清timer0中断标志
TMR0=0xE0; /*置初值 20KHz的半个周期为25us*/
T0IE=1; //timer0中断允许
GIE=1; //开全局中断
}
void interrupt isr(void) //中断子程序
{
TMR0=0xE0; //定时器0置初值25us
T0IF=0; //清中断标志
GP0=~GP0; //GP0位取反
GP1=!GP0; //使GP1引脚取反GP0
/*在这里废寝忘食3总天,一度引起孙子的奶奶发飙
也没有折腾出正确的结果*/
}
void main(void) //主程序
{
IO_init(); //引脚初始化函数
timer0_init(); //定时器0初始化函数
while(1) //死循环
{
}
}
写入单片机;手工搭建最小系统上示波器看波形结果频率是26.3KHz,GP0引脚正占空比只有12.6%,负占空比87.3%;GP1引脚正占空比86.3%,负占空比13.6%幅值5.04V
删除中断子程序里的GP1=!GP0语句结果GP0引脚示波器上只有一条5.17V的直线没有波形了,频率显示为小于10Hz.
大家说怪不怪!!本应该GP0输出信号不受影响的怎么就没有了呢????
接下来就是折腾了反复换TMR0的初值,最后试到0xE0时频率是19.96KHz正占空比9.6%负占空比是90.4%,也就是说正脉冲的宽度是4.8us这个值基本是恒定的试了下,从10KHz到30KHz这个值不变.然后大发现这个4.8us竟然是执行GP1=!GP0 这条语句所耗费的时间,所以上述删除这条语句示波器上只显示一直线。
-
GAS0002副本.jpg
(105.12 KB, 下载次数: 114)
作者: xxll 时间: 2021-11-24 11:31
本帖最后由 xxll 于 2021-11-24 12:03 编辑
现在的问题就是怎样才能使正负波形占空比为50% ?
于是我又timer2这个定时器来做程序如下:
#include <pic.h>
#define uchar unsigned char
#define uint unsigned int
uchar TA,TB;
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
}
void timer2_init(void) //fosc/4 8M所以计数周期为0.5us
{
T2CON=0b00000101; //timer2控制寄存器设置
PR2=24; //25us比较数字
PEIE=1; //模块中断使能
TMR2IE=1; //定时器2中断使能
GIE=1; // 开全局中断
}
void interrupt isr(void)
{
GP1=~GP1;//中断后设引脚反相
GP0=~GP1;//使GP0与GP1反相
TMR2IF=0;/*TMR2溢出标志清零*/
}
void main(void)
{
__CONFIG(FOSC_INTOSCIO&WDTE_OFF);//系统配置,使用内置时钟,关看门狗
OSCCONbits.IRCF0=1;
OSCCONbits.IRCF1=1;
OSCCONbits.IRCF2=1;//此3条设置时钟为8M
IO_init();//端口初始化
timer2_init();//timer2初始化
GPIO=0X00;//全部端口设为0
while(1) //死循环
{
}
}
-
GAS0003副本.jpg
(105.41 KB, 下载次数: 107)
作者: 188610329 时间: 2021-11-24 13:42
PIC 没玩过,本不该发言,但是看你帖子实在冷清,深感年纪大了学单片机之不易……,
作为70后,在你身上看到10年后的自己,所以多多少少想帮你顶一把。
虽然PIC没有玩过, 有些道理应该是互通的所以说两句。
GP0=~GP0; //GP0位取反
GP1=!GP0; //使GP1引脚取反GP0
基于单片机的工作原理,这里很可能得不到你希望的结果…… 举个简单的例子。
GP0 = ~GP0; 这里的等号左边 和 等号右边,其实是不同的东西。
等号的右边,是当前GP0的电平状态,综合了外部电平和内部设置之后的最后结果,然后通过~取反
等号左边,是你给GP0 设置的新状态即会改变GP0的内部电平关系,然后通过这个内部关系再去改变端口的外部关系。
当你运行完 GP0 = ~GP0 立刻运行 GP1=!GP0; 此时读取的 GP0, 如上所说是结合内部设置和外部电平综合后的一个状态,而不是你刚刚赋值的结果,由于各种单片机不同我不能说100%,但是有很大的概率,这个时候的GP0,是并没有完成 GP0 = ~GP0 这个操作后更新 的状态。 但,也有可能已经完成了更新。这个不好判断,所以在时序允许的情况下,一般应该这么写代码。
GP1= GP0; //使GP1引脚取GP0
GP0= !GP0; //GP0位取反
这样能保障,两次读到的GP0 是相等的。
如果,你一定要先改GP0, 再改GP1 那么也可以用一个中间变量
F0 = GP0;
GP0 = !F0;
GP1 = F0;
这样是比较安全的操作。
作者: AUG 时间: 2021-11-24 13:55
本帖最后由 AUG 于 2021-11-24 14:01 编辑
从你这个图可以看出,那个脉冲是你每次进去之后GP0=~GP0取反的结果,后面变低是因为你又GP1=!GP0,使其变低了.建议如下操作
void main()
{
timer_init();//25us一次中断 40KHz
GP0 = 1; //起始值相反
GP1 = 0;
while(1);
}
interrupt timer(void)
{
//重装计数值
//清中断
GP1=GP0;
GP0=~GP0;
}
作者: qiangtech 时间: 2021-11-24 14:18
用过一个仿PIC的芯片。
用的T0做的510US定时:
用的T1做的38KHz的方波:
中断里的IO翻转。
作者: qiangtech 时间: 2021-11-24 14:19
不确定寄存器完全一致的,你可以参考一下,看着修改。
作者: Y_G_G 时间: 2021-11-24 14:26
中断程序这样改试一下:
void interrupt isr(void) //中断子程序
{
TMR0=0xE0; //定时器0置初值25us
T0IF=0; //清中断标志
GP1=GP0; //使GP1引脚取反GP0后的GP0
GP0=~GP0; //GP0位取反
}
其它参考意见:
PIC我用过,个人感觉PIC实在不适合单片机入门,更多的是工业产品才用的,很多操作是有严格要求的,比如硬件堆栈,甚至很多学单片机的对堆栈也只是百度大概的看一遍,但真正到PIC硬件堆栈的时候,问题就出来了
IO的数据方向要注意,中断只有一个入口.......
数据手册是肯定要一边写代码一边看的,但PIC的8位单片机数据手册都是汇编的,印象中是没有C的,所以,汇编指令最好是也要会一点,不要求能写程序,至少对着指令能看明白
作者: hz_dyg 时间: 2021-11-24 15:11
一般来说,端口设置和读取的寄存器是分开的,设置后马上读取,单片机速度太快,读取状态值不一定是稳定后的值。最好定义个变量 bit status, 中断里改变status = !status; 然后赋值给端口寄存器
作者: yzwzfyz 时间: 2021-11-24 15:12
增加一个中间变T.
每次中断后:
GP0=T
T=!T
GP1=T
试试看。
作者: yzwzfyz 时间: 2021-11-24 15:13
如果成功,再告诉你原因。不成功就罢了。
作者: 天ノ忆 时间: 2021-11-24 15:25
90后,来一个最简单的方法,因为手中没有示波器,只好麻烦楼主验证一下了,如果还是不行就自动忽略后面的吧,一般来说对定时器要求较高,定时器中断里处理的代码越少越好,能少就尽量少,以下是代码- #include <pic.h>
- char i = 0;
- void IO_init(void) /*端口初始化*/
- {
- TRISIO=0x00;
- GPIO=0X00;
- }
- void timer0_init(void) //定时器0初始化使用系统默认时钟4M fosc/4 所以计数周期为1us
- {
- T0CS=0; //timer0工作于定时器方式
- PSA=1; //timer0不分频
- T0IF=0; //清timer0中断标志
- TMR0=0x01; /*置初值 20KHz的半个周期为25us*/
- T0IE=1; //timer0中断允许
- GIE=1; //开全局中断
- }
- void main()
- {
- IO_init(); //引脚初始化函数
- timer0_init(); //定时器0初始化函数
- while()
- {
- if(i >= 50) {
- i = 1;
- }else if(i >= 25) {
- GP0 = 1;
- GP1 = 0;
- }else if(i >= 0) {
- GP0 = 0;
- GP1 = 1;
- }
- }
- }
- void interrupt isr(void) //中断子程序
- {
- TMR0 = 0x01; //定时器0置初值1us
- T0IF = 0; //清中断标志
- i ++;
- }
复制代码
作者: xxll 时间: 2021-11-24 16:38
首先谢谢你,你这种方法我之前也试过,正脉冲的宽度只增加2.5us
作者: xxll 时间: 2021-11-24 17:26
感谢您的参与,你说的第一种GP0=~GP0;GP1=!GP0;刚试过结果脉宽没有改变.这种顺序改变在示波器上观看没有变化的.当然,我知道编程是要讲究时序的,否则实际应用中会出大问题的!至于第二种方法我在早些时候也试过,中间加一个变量操作,确实增加了2.5us宽度.再次谢谢!
作者: xxll 时间: 2021-11-24 17:53
本帖最后由 xxll 于 2021-11-25 07:00 编辑
谢谢各位老师,在这么短的时间内就有许多人参与,出乎我的意料,心情非常激动!大家的建议我一定抽时间一一测试总结经验.再报告给各位.开先也偿试出一个接近答案的程序,但是他不符合中断的原则.CPU在正程中断子程序里总总耗费25us.现在也把这个程序给贴出来:
#include <pic.h>
#define uchar unsigned char
#define uint unsigned int
uchar TA,TB;
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
}
void timer2_init(void) //fosc/4 8M所以计数周期为0.5us
{
T2CON=0b00000101; //timer2控制寄存器设置
PR2=24; //25us比较数字
PEIE=1; //中断使能
TMR2IE=1; //中断使能
GIE=1; // 开全局中断
}
void delay(void)//延时函数
{
uchar i;
for(i=2 ;i ; i--);
}
/*************************************************/
/*中断子程序正脉宽为24.6us负脉宽为25.626us,频率为19.92KHz*/
/*************************************************/
void interrupt isr(void)
{
GP1=~GP1;//中断后设引脚反相
delay(); //延时
TA=GP1;//此项也是为延时2.5us
GP0=~TA;//使GP0与GP1反相
TMR2IF=0;/*TMR2溢出标志清零*/
}
void main(void)
{
__CONFIG(FOSC_INTOSCIO&WDTE_OFF);//系统配置,使用内置时钟,关看门狗
OSCCONbits.IRCF0=1;
OSCCONbits.IRCF1=1;
OSCCONbits.IRCF2=1;//此3条设置时钟为8M
IO_init();//端口初始化
timer2_init();//timer2初始化
GPIO=0X00;//全部端口设为0
while(1) //死循环
{
}
}
-
GAS0004副本.jpg
(103.03 KB, 下载次数: 107)
作者: xxll 时间: 2021-11-25 07:25
你这种思路很好,但是无法实现!先不说你的初值不对,就中断定时1US哪个单片机都难实现。本单片机时钟4M从响应中断开始压栈执行子程序到出栈要9到15us。这还要求子程序特别简单高效才可。
作者: yzwzfyz 时间: 2021-11-25 07:59
增加一个8位的中间 N。初始化时:N=01。 ;目的:对应 GP1=0,GP0=1。
每次中断后:
GPIO=N
N= N XOR 3 ;目的:对应的 GP1、GP0 求反,准备下次使用。
试试看。
要点:让GP1、GP0 同时改变。
作者: lids 时间: 2021-11-25 09:29
仿真试了下,一路方波正常,两路波形就乱了
#include <pic.h>
__CONFIG(0x0054);
#define uchar unsigned char
#define uint unsigned int
uchar flag,flag1;
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
GP0=0;
GP1=0;
}
void timer0_init(void) //定时器0初始化使用系统默认时钟4M fosc/4 所以计数周期为1us
{
T0CS=0; //timer0工作于定时器方式
PSA=1; //timer0不分频
T0IF=0; //清timer0中断标志
TMR0=224; /*置初值 20KHz的半个周期为25us*/
T0IE=1; //timer0中断允许
GIE=1; //开全局中断
}
void main(void) //主程序
{
IO_init(); //引脚初始化函数
timer0_init(); //定时器0初始化函数
while(1) //死循环
{
if(flag)
{
GP0=1;
// GP1=!GP0;
}
else
{
GP0=0;
// GP1=!GP0;
}
/*
if(flag1)
{
GP1=0;
}
else
{
GP1=1;
} */
}
}
void interrupt isr(void) //中断子程序
{
T0IF=0; //清中断标志
TMR0=224; //定时器0置初值25us
flag = !flag;
}
-
51hei截图20211125092516.jpg
(93.61 KB, 下载次数: 106)
作者: dzbj 时间: 2021-11-25 12:29
老哥好 PIC一点都不懂 帮不上 我也60后 和您打个招呼
作者: xxll 时间: 2021-11-25 13:13
谢谢提供方法!刚刚测试了一下,编译通不过,将配置语句删除,使用默认的时钟通过编译,用示波器看了一下,单脚输出频率只有10KHZ且不稳定,占空比似乎是50%但不稳定左右飘忽,怎么调示波都不能很好的同步……
作者: 188610329 时间: 2021-11-25 17:28
PIC12F683 的PDF手册有么? 发一个上来,我研究一下看看。毕竟有些东西受限于硬件,光看代码可能忽略掉一些关键信息。
作者: xxll 时间: 2021-11-25 17:49
-
-
12f683cn.pdf
3.14 MB, 下载次数: 7
作者: xxll 时间: 2021-11-25 18:57
兄弟好!
作者: 188610329 时间: 2021-11-25 19:29
手册看了一下,发现一个被忽略的问题。
这个片子,只有35条指令,其中对位的操作指令,只有4条,而这个片子速度还比较慢,周期比较长。带来的直接结果就是:
GP1=~GP1;//中断后设引脚反相
GP0=~GP1;//使GP0与GP1反相
这看似简单的两个操作,实际上需要消耗非常多的时间来处理。
通过仔细分析这个片子拥有的35条指令,我认为:
初使状态 GP0 = 0, GP1 = 1 之后。在翻转的时候,使用:
GPIO ^= 0x03; 这一条指令来取代 你的:
GP1=~GP1;//中断后设引脚反相
GP0=~GP1;//使GP0与GP1反相
这两条指令,是比较高效的方法。建议你尝试一下。
作者: xxll 时间: 2021-11-26 08:19
按照你的方法试了一下,没有波形输出只有直流电压。
其实这个真正的原因,我个人认为不是操作快慢的问题,而是CPU离开中断子程序输出引脚信号立即再次翻转的问题。像前面我示出的一个例子,在子程序里延时即可得到想要的波形。
这个程序原意是:1。CPU给引脚付值然后延时,CPU跳出再干别的事。等延时到CPU在回去给相关引脚重新付值,就这样周而复始下去的。现在的问题就是在正程时间的延时都给了逆程时间了。
如将子程序中指令位置上下调换,只影响逆程时长,不影响正程脉宽
TMR0=224;
T0IF=0;
GP1=!GP1;
GP0=!GP1;
这样的正脉冲是4.8US,负脉宽是50.2US,频率为19.9KHz;
GP1=!GP1;
GP0=!GP1;
TMR0=224;
T0IF=0;
这样调换一下顺序,正程脉宽仍然是4.8us,但负脉宽变成了54.4us,频率降到16.8KHz;
TMR0=224;
T0IF=0;
GP1=!GP1;
TA=!GP1;
GP0=TA;
在这段里加入一个变量TA,正脉宽会变成5.48us,负脉宽缩小,频率不变仍是19.Khz;
你看从这里能不能想出解决方案来!
作者: xqleft 时间: 2021-11-26 09:20
我现在用得单片机刚好是PIC架构得。我看了半天不知道理解对不对。你想GP1输出的正占空比,GP0输出的正占空比是GP0的负占空比时间,假如1现在的正占空比是10%,那么GP0输出的正占空比就应该是90%对吧。您是这意思么?
作者: taotie 时间: 2021-11-26 09:38
一路方波正常很好~~加个7404反相器就成了互补波形了
作者: xxll 时间: 2021-11-26 09:41
我要的就是一个引脚上正50%,负50%就这样简单,要求是用定时器0实现,频率20KHZ
作者: Y_G_G 时间: 2021-11-26 14:36
还在折腾这个?
TMR0=0xE0(224),定时器就是256-224=32
如果你是用4MHZ时钟,那么,定时器的中断时间是不低于32uS的,你这还怎么达到?20KHZ的输出?
TMR0=0xE0; /*置初值 20KHz的半个周期为25us*/..........这是怎么算出来的?
而且,PIC是没有堆栈指令的,编译器可能还要加几条保存W和其它会改变的文件寄存器的指令
这样一来,你这个是肯定不会有20KHZ的频率的
而且,在中断程序中,尽量少用变量计算,没有用过PIC的C,不知道PIC有没有位,如果有位,就用位来判断,定时器设定要高于20KHZ,TMR0的值要大于0xe7
先声明一个位
void interrupt isr(void) //中断子程序
{
TMR0=240; //定时器0置初值,先看结果,再慢慢调节
T0IF=0; //清中断标志
if(ON)
{
GP0=1;
GP1=0;
ON=0;
}
else
{
GP0=0;
GP1=1;
ON=1;
}
}
作者: xxll 时间: 2021-11-26 18:19
这位老师好!我把你的方案和数据测试结果汇报给你:时钟4M时,GP0正脉宽0.8us负脉宽75.2us频率13KHz;
GP1正脉宽38.8us,负脉宽37.6us,频率13KHZ
时钟设置成8M时:GP0正脉宽0.5us(500ns),负脉宽示波器没报,频率26.3KHZ;
GP1正脉宽19.4us,负脉宽18.6us,频率26.3KHz
老师从这二种时钟数据看,只有GP1正负脉冲占空比能接近50%,而GP0正脉冲太窄,你看如何解决?
作者: Y_G_G 时间: 2021-11-26 18:54
你把完整的代码上传一下
GP0和GP1是一样的调整,在时间上应该是一致的,没有理由只有一个行,一个不行的,要么就都不行,要么就都行
你也可以自己调试一下的呀,把GP1代码删除,或者把GP0换成其它的端口试一下
void interrupt isr(void) //中断子程序
{
T0IF=0; //清中断标志
if(ON)
{
GP0=1;
GP1=0;
ON=0;
}
else
{
GP0=0;
GP1=1;
ON=1;
}
TMR0=240; //定时器0置初值,先看结果,这设置放后面
}
作者: xxll 时间: 2021-11-26 20:44
正半周25us+负半周25us,总周期是50us哪来的40KHZ,这个单片机20KHZ都可能做不督到,还40K
双脚输出互补的方波如果拿来驱动逆变半桥,GP1=GP0想想是什么后果?再都为了试验,前面说过都试过了,没有变化,是一样的。这东西折腾很久了,不是我执着,论个所以然,早就放弃了……
作者: 188610329 时间: 2021-11-26 21:34
老哥, 你说…… ,会不会是因为:
ANSEL = 0; 这句没写的关系啊?
我琢磨了很久,你在顶楼说的,删除了GP1 = !GP0; 就没输出了, 你觉得不应该,我也觉得不应该,所以,我在想,是不是一开始咱们就没找对方向? 其实就是端口的初始化没有做好? 所以没有输出?
作者: xxll 时间: 2021-11-27 09:08
ANSEL寄存器是为模拟信号输入设置寄存器和它没关系,端口就这两个寄存器TRISIO和GPIO
作者: xxll 时间: 2021-11-27 09:23
#include <pic.h>
#define uchar unsigned char
uchar ON;
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
GPIO=0X00;
}
void timer0_init(void) //定时器0初始化使用系统默认时钟4M fosc/4 所以计数周期为1us
{
T0CS=0; //timer0工作于定时器方式
PSA=1; //timer0不分频
T0IF=0; //清timer0中断标志
TMR0=224; /*置初值 20KHz的半个周期为25us*/
T0IE=1; //timer0中断允许
GIE=1; //开全局中断
}
void interrupt isr(void) //中断子程序
{
TMR0=224; //定时器0置初值25us
T0IF=0; //清中断标志
if(ON)
{
// TMR0=224; //定时器0置初值25us
// T0IF=0; //清中断标志
GP2=0;
GP1=1;
ON=0;
}
else
{
// TMR0=224; //定时器0置初值25us
// T0IF=0; //清中断标志
GP1=0;
GP2=1;
ON=1;
}
// GP0=~GP0; //GP0位取反
// GP1=!GP0; //使GP1引脚取反GP0
/*在这里废寝忘食3总天,一度引起孙子的奶奶发飙
也没有折腾出正确的结果*/
}
void main(void) //主程序
{
OSCCONbits.IRCF0=1;
OSCCONbits.IRCF1=1;
OSCCONbits.IRCF2=1;//此3条设置时钟为8M
IO_init(); //引脚初始化函数
timer0_init(); //定时器0初始化函数
GP1=1;
GP2=0;
while(1) //死循环
{
}
}
花括号里的顺序谁在前谁就是窄脉冲,和TMR0、T0IF所在位置无关,但和频率高低有较大的关系。稍后我将发个统计表格上来供大家研究!!
-
GAS0002副本.jpg
(112.94 KB, 下载次数: 110)
-
GAS0004副本.jpg
(116.43 KB, 下载次数: 150)
作者: Y_G_G 时间: 2021-11-27 12:00
你试过增加ANSEL=0x00;这语句没有?
ANSEL 是要先清除的!
作者: Y_G_G 时间: 2021-11-27 12:25
还要添加一条:
CMCON0=0x07;
作者: xxll 时间: 2021-11-27 19:56
关于这里的TMRO=224是因为时钟是8M时设的值,(256-224)*0.5=16us在8M时钟时CPU执行中断大约耗费9us (后面也证实了),16+9=25
作者: 188610329 时间: 2021-11-27 20:17
从你发的手册上看,
ANSEL 控制引脚是模拟输入,还是数字输出。
如果 有 位置1则为模拟输入,如果该位置0则为数字输出。
手册上看,上电后 ANSEL 的初值不为0。
并且,手册上还说,上电后端口初始化 ANSEL 需要 置 0 复位。
另外,和GPIO有关的寄存器,总共有11个,
此表中,没有打阴影的部分,都是会影响GPIO的输出的设置。
通过此表也能看到,ANSEL 不设置0的话,GPIO0~GPIO3 默认为模拟输入。
作者: taotie 时间: 2021-11-27 22:34
虽然占空比没达到百分之50,但已经是互补方波了
/* Main.c file generated by New Project wizard
*
* Created: 周六 11月 27 2021
* Processor: PIC12F683
* Compiler: HI-TECH C for PIC10/12/16
*/
#include <pic.h>
__CONFIG(0x0054);
#define uchar unsigned char
#define uint unsigned int
uchar flag,flag1;
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
GPIO==0b00000000;
}
void timer0_init(void) //定时器0初始化使用系统默认时钟4M fosc/4 所以计数周期为1us
{
T0CS=0; //timer0工作于定时器方式
PSA=1; //timer0不分频
T0IF=0; //清timer0中断标志
TMR0=224; /*置初值 20KHz的半个周期为25us*/
T0IE=1; //timer0中断允许
GIE=1; //开全局中断
}
void main(void) //主程序
{
IO_init(); //引脚初始化函数
timer0_init(); //定时器0初始化函数
while(1) //死循环
{
if(flag)
{
GPIO=0B00000001;
}
else
{
GPIO=0B00000010;
}
}
}
void interrupt isr(void) //中断子程序
{
T0IF=0; //清中断标志
TMR0=224; //定时器0置初值25us
flag = !flag;
}
作者: taotie 时间: 2021-11-28 00:10
本帖最后由 taotie 于 2021-11-29 14:49 编辑


相差无几
时钟8Mhz,标志判断的内容由死循环转到中断子程序中。
初值设置为225 方波频率=20000hz
* Created: 周六 11月 27 2021
* Processor: PIC12F683
* Compiler: HI-TECH C for PIC10/12/16
*/
#include <pic.h>
__CONFIG(0x0054);
#define uchar unsigned char
#define uint unsigned int
uchar flag,flag1;
void IO_init(void) /*端口初始化*/
{
TRISIO=0x00;
GPIO==0b00000000;
}
void timer0_init(void) //定时器0初始化使用系统默认时钟4M fosc/4 所以计数周期为1us
{
T0CS=0; //timer0工作于定时器方式
PSA=1; //timer0不分频
T0IF=0; //清timer0中断标志
TMR0=225; /*置初值 20KHz的半个周期为25us*/
T0IE=1; //timer0中断允许
GIE=1; //开全局中断
}
void main(void) //主程序
{
IO_init(); //引脚初始化函数
timer0_init(); //定时器0初始化函数
while(1) //死循环
{
}
}
void interrupt isr(void) //中断子程序
{
//T0IF=0; //清中断标志
TMR0=225; //定时器0置初值25us
{
if(flag)
{
GPIO=0B00000001;
}
else
{
GPIO=0B00000010;
}
T0IF=0;
}
flag = !flag;
}
作者: xxll 时间: 2021-12-2 08:22
大家好,由于这几天忙没有及时跟帖。在这里谢谢大家对我学习的支持,尤其是@ 188610329 和@ Y_G_G 两位网友老师。在你们细心的引导下,使我的问题得到正确的答案:端口没有正确的初始化!是我没有认真细心的阅读说明书,造成的后果。只要在IO_init()函数内加入ANSEL=0X00;与CMCON0=0x07;(尤其是CMCON0这个寄存器绝对不能不初始化)我的问题基本就解决了……
-
GPIO端口.jpg
(196.11 KB, 下载次数: 220)
作者: xxll 时间: 2021-12-2 08:45
你的程序我也测试了,示波器上的波形不稳定,一会正常一会跳变成窄宽不等的波形。还有你这种方式我个人觉得不妥,如果在主程序中加入按键扫描和指示灯闪烁,这种方式就更无法正常输出频率稳定的方波了。你认为呢?
作者: xxll 时间: 2021-12-2 11:05
这个波形图是中断子程序
void interrupt isr(void) //中断子程序
{
TMR0=224;
T0IF=0;
GP1=!GP1;
GP2=!GP1;
}
之图
GP1正脉宽为24.8us,负脉宽为25.4us,正负之差0.6uS;GP2比GP1滞后3us
这时GP1与GP2合成的波形即示波器探头接GP1地夹GP2
下面是网友
Y_G_G 提供的子程序波形图
void interrupt isr(void) //中断子程序
{
TMR0=224; //定时器0置初值25us
T0IF=0; //清中断标志
if(ON)
{
GP2=0;
GP1=1;
ON=0;
}
else
{
GP1=0;
GP2=1;
ON=1;
}
GP1正负脉宽差也是0.6us,但GP1与GP2合成波延时时差不明显
下面是一款小产品上测得的波形它的正负脉宽差只有0.2us
合成波形很顺滑没有中间的延迟现象
到此我又有新的问题要问了:这个产品的波形是怎样做到呢?
作者: Y_G_G 时间: 2021-12-2 11:26
这GP1和GP0是在同一个端口的吗?没有看数据手册,不知道
不同端口的话,那是要两条指令,那是肯定要有延时的
如果是同一个端口,那就是一条指令就行了if(ON)
{
PORTD=0x01;//假设是RD端口输出
ON=0;
}
else
{
PORTD=0x02;
ON=1;
}
作者: xxll 时间: 2021-12-2 11:43
本帖最后由 xxll 于 2021-12-2 12:03 编辑
不是同一端口,原来是GP0与GP1,现在改为GP1与GP2了,其实道理是一样的厅
是我理解错了是同一个端口寄存器,因为只有8个引脚的单片机。
作者: Y_G_G 时间: 2021-12-2 12:03
GPIO就是一个端口的好不好,直接赋值GPIO端口应该就可以得到你要波形了{
GPIO=0x01;//GP0 GP1输出,自己也可以改其它的IO,如果不想影响其它IO,可以用&
ON=0;
}
else
{
GPIO=0x02;
ON=1;
}
作者: 188610329 时间: 2021-12-2 13:40
老哥,如果你要实现 产品副本.jpg 那样的波形的话,你改写端口的指令必须修改。
void interrupt isr(void) //中断子程序
{
TMR0=224;
T0IF=0;
GPIO |= 0x06; // 要用字节改写 GPIO总线, 而不是用位改写 IO
}
作者: xxll 时间: 2021-12-2 17:44
你好,这个或运算是如何实现的,我不懂啦。如0x06=0b00000110,若原GPIO=0b00000010或运算后它不还是0b00000110吗?
作者: Y_G_G 时间: 2021-12-2 17:49
GPIO |= 0x06;
这个是不行的,或指令不能输出低电平的
你自己也要思考一下的嘛
肯定是要用&的嘛
作者: 188610329 时间: 2021-12-2 17:56
Y_G_G 发表于 2021-12-2 17:49
GPIO |= 0x06;
这个是不行的,或指令不能输出低电平的
你自己也要思考一下的嘛
打错了gpio^=0x06
作者: xxll 时间: 2021-12-2 17:58
我不说过我是个门外汉吗!与我也不会呀
作者: 188610329 时间: 2021-12-2 18:03
xxll 发表于 2021-12-2 17:44
你好,这个或运算是如何实现的,我不懂啦。如0x06=0b00000110,若原GPIO=0b00000010或运算后它不还是0b000 ...
抱歉,是我的锅,没复验就发了。
应该是
gpio ^= 0x06; 与或计算,即1变0,0变1。手机发帖排版大小写就不修改了。
作者: 天ノ忆 时间: 2021-12-2 18:04
GPIO |= 0x06;//这个1,2位输出高电平,不改变其他位
GPIO &= 0XF9;//这个1,2位输出低电平,同样不改变其他位
作者: Y_G_G 时间: 2021-12-2 18:54
门外汉不是理由
与或非编程基础类知道,不管是你半路出家还是科班出身,不管你专业人士还业余爱好,也不管你是长期爱好还是一时性起
这基础知道是肯定要学的,如果不想学,那就不要搞单片机了
不是打击你,而是没有基础的话,那这单片机玩起来实在太累了,还浪费时间
从你这个问题就可以看出来了,如果当初,你多花一个小时的时间去看一下端口设置,那你就不会在这50%占空比上面花那么多的时间和精力
个人感觉,很多时候,一个自己几天甚至半个月都搞不定的东西,很有可能只是一个简单的东西而已,只是自己没有严谨认真的去看相关的资料而已
我之前就经常碰到这样的问题,一个小小的东西,搞几天都搞不好,怎么找都找不到问题,最后干脆之前的代码都不要,从头再看资料,一步一步的来,一下子就发现问题了,而且,不过是一个很基础很简的问题而已,只是自己老是钻牛角尖而已
所以,基础很重要,不管你多大年纪,不管你是出什么目的学单片机,多看一下基础,不会有坏处
欢迎光临 (http://www.51hei.com/bbs/) |
Powered by Discuz! X3.1 |