本文写于 2018年11月19日,距今已超过 1 年,距 2020年03月27日 的最后一次修改也已超过 3 个月,部分内容可能已经过时,您可以按需阅读。如果图片无法显示或者下载链接失效,请给我反馈,谢谢!


5 8 投票数
评分

工程07_01 :GPIO中断

首先需要将所有的 P1.1 修改为 P1.4,即将(GPIO_PORT_P1,GPIO_PIN1)修改为(GPIO_PORT_P1,GPIO_PIN4)

特别需要注意的是,中断处理程序中的if(status & GPIO_PIN1)也需要修改。

然后把所有的 LED1 换成 LED2 的红灯,即将(GPIO_PORT_P1,GPIO_PIN0)换成(GPIO_PORT_P2,GPIO_PIN0)

需要修改的地方有点多,不能漏。

实验的效果与上一次实验一致——绿灯闪烁 2 次,随后红灯闪烁。随着按键按下,闪烁间隔加大。直到间隔大于“喂狗”周期,看门狗复位。复位后现象循环。区别只在 LED1 换成了LED2 的红灯,以及,S1 按键换成了 S2。

工程07_02:MCLK时钟配置

只需要将 switch-case 的顺序颠倒过来。

switch(count)
{
    case 0:
        tmp=CS_CLOCK_DIVIDER_128; //不分频
        break;
    case 1:
        tmp=CS_CLOCK_DIVIDER_64; //2分频,以下类推
        break;
    case 2:
        tmp=CS_CLOCK_DIVIDER_32;
        break;
    case 3:
        tmp=CS_CLOCK_DIVIDER_16;
        break;
    case 4:
        tmp=CS_CLOCK_DIVIDER_8;
        break;
    case 5:
        tmp=CS_CLOCK_DIVIDER_4;
        break;
    case 6:
        tmp=CS_CLOCK_DIVIDER_2;
        break;
    case 7:
        tmp=CS_CLOCK_DIVIDER_1;
        break;                
    default:
        return ;
}

实验效果是 LED2 的绿灯闪烁频率改为周期性的由慢变快。

一个需要说明的地方是,上电复位的时候,时钟频率还是快的,如果想要上电复位时候就是慢闪,那么在无限循环之前,就应该将始终频率设置一下。

MAP_CS_initClockSignal(CS_MCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_128); //如果想要上电复位时候就是慢闪,这里也要修改时钟频率

工程07_03:交通信号灯的实现

有这么几个关键的地方:

  • 黄灯怎么亮
  • 如何快速切换到绿灯并保持后面的执行顺序一致

首先明确怎么亮黄灯。

可以红灯和绿灯一起亮,那就是黄灯的效果。

当然也可以用 P2.3 的蓝灯代替黄灯。

或者也可以三个灯一起亮,就是白灯,也可以自己调出紫色的灯等等……

明确了这一点以后,就先可以把红绿灯切换的函数写出来。

先不考虑中断。那么就是:

switch (color){
        case 0:
            GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN0); //红灯灭
            GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN1); //绿灯亮
            MyDelay(100); // 空循环100000
            break;
        case 1:
            GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0); //红灯亮,2灯同时亮就是黄灯
            GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN1); //绿灯亮,2灯同时亮就是黄灯
            MyDelay(15); // 空循环15000
            break;
        case 2:
            GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0); //红灯亮
            GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN1); //绿灯灭
            MyDelay(150); // 空循环150000
            break;
    }

这段代码在作用是,根据 color 的颜色不同,决定亮哪些灯。举例来说,如果 color 当前是绿色,那么就关掉红灯、打开绿灯,然后延时绿灯的秒数。对于其他两种颜色的等也是类似的。

在执行完当前的颜色后,color 加 1,并对 3 取模,实现了0、1、2的循环。

color = (color + 1 ) % 3;

需要注意的是,样例代码给的是初始化 i=1。为了方便操作,可选地可以让 i 改成从 0 开始。

然后就要加中断。

首先是 S1 中断,频率由慢变快。

就是实验02的内容。

与实验02类似地,将 switch-case 的顺序颠倒过来。

然后调整时钟频率。

MAP_CS_initClockSignal(CS_MCLK, CS_DCOCLK_SELECT, tmp); 
count++;
count %= 8;

再添加一个中断,需要前面先声明一下。

前面已经有了一个按键的中断,只需要补上另一个按键即可。

MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN1);  //设置P1.1(s1按键)为输入模式并拉高
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P1, GPIO_PIN1);               //清除P1.1中断 
MAP_GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN1);                  //使能GPIO1.1中断MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN4);  //设置P1.4(s2按键)为输入模式并拉高
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P1, GPIO_PIN4);               //清除P1.4中断 
MAP_GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN4);                  //使能GPIO1.4中断

注意这句只要执行一次,因为他们都是一个组的。

MAP_Interrupt_enableInterrupt(INT_PORT1);

对于按键 S1,一种简单的做法就是直接在中断处理程序令 color=0

这样的话,中断处理程序就是

if(status & GPIO_PIN1) //判断P1.1按键(S1键)是否按压下
    {       
        if (color != 0)
            color = 0;
    }

这是最简单的处理方式,事实上也可以被认为是正确的处理方式。

但是这样会有潜在的问题,不够优雅。

试想,假设现在处于某红灯的 Delay() 函数中,按下了按键,触发中断,此时设置 color 为绿色,然后中断返回,返回到刚才执行的地方,继续执行,于是刚才剩下的循环就会继续执行完,这显然不是预期的,并且当执行完延时以后,由于下面有 color = (color + 1 ) % 3; 相当于又把 color 设置掉了,因此,刚才中断处理程序的 color=0 就没意义了。

一种解决方案自然是在中断中完成转换到绿灯。

例如,我们可以直接在中断处理程序中写上

GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN1); 

MyDelay(100);

但是并没有根本性解决问题:中断返回以后还是不知道在哪里。

所以可不可以在中断中进行复位?那不就是回到原点了么。

不好,因为会把时钟频率也修改掉。复位以后又回到起点,意味着刚才用另一个按键修改的系统时钟频率也会被重置。

这显然也不优雅。

问题的根源其实是在于,怎么知道中断执行了,换句话说,主函数如何知道当前是正常执行的状态,还是刚刚从中断退出了。

函数的返回值。

然而,中断没有返回值。

所以可以考虑开一个全局的变量。

例如我使用 flag 作为全局变量,标识当前是中断返回的状态。

默认状态是 flag=0,一切如常。

当进入中断的时候,设置 flag=1

if(status & GPIO_PIN1) //判断P1.1按键(S1键)是否按压下
    {       
        if (color != 0)
           flag = 1;
    }

中断返回。

主函数判断当前如果是由中断返回的,那么,color 重置为绿色,且清空 flag,否则,说明程序是正常执行的,那就 color 累加并对 3 取模。

if (flag)
        color = flag = 0;
    else 
        color = (color + 1 ) % 3;
    }

有了这个全局变量,上面提到的问题也解决了。

如果程序是在 Delay() 中,那么中断返回以后,我期望的效果是立刻切换到绿色灯,而不要做之前剩下的等待了。

所以很简单地,循环照常做空循环等待,但是如果 flag 是 1,就 break 出来。

//延时函数
void MyDelay(unsigned int count)
{
    volatile uint32_t i;
    for(i=1000*count; i>0; i--){
        if (flag)
            break;
    }
    return ;
}

另外可能遇到一个问题,那就是出现 Memory cannot **** 错误,也就是代码写不到开发板上。

这是版本的问题。

如果 1.0 的板子,使用CMSIS,没事。用 1.2 的板子,使用TI XDS也没事。但是实验环境只有CMSIS,没有TI,如果版本是 1.2,也只能用CMSIS,这种情况就可能会导致烧写失败,出现Memory cannot ***错误。

这种时候,只要用自己的电脑,装了新版的 Keil 和新版的 SDK,并且固件升级到了 1.2,那么用TI XDS Debugger就不会有问题。

但是如果一定要在机房环境调试,那么,借同学的电脑,让他用 1.2 的TI XDS Debugger随便烧录一个程序进去,烧写一次,再回来,就又可以写的进去了。

完整代码下载:

5 8 投票数
评分
28条留言
订阅评论
提醒
guest

在点击发表评论按钮时,网络请求的数据包含浏览器版本、操作系统版本和 IP 地址;您的网络服务提供商、雇主或学校、政府机构可能会看到您的访问活动;根据浏览器默认行为、操作系统设置和安全防护软件的设置不同,您的浏览器可能会也可能不会在本地 Cookies 缓存您输入的用户名、邮箱以便下次评论使用。

请对自己的言行负责。

您想以什么身份发表评论
邮箱将在您的评论被回复时给您通知
(可选)如果您也有个人网站,不妨分享一下
我对这篇文章的评分
这篇文章给您带来多大帮助
28 评论
内联反馈
查看所有评论
1
1 (@guest_1115) Chrome 79.0.3941.4 Windows 10
游客
2019年11月22日 17:29

tql

billbiscuit
billbiscuit (@guest_1114) Edge 18.18362 Windows 10
游客
2019年11月22日 17:21

666

hahaha
hahaha (@guest_1083) Chrome 78.0.3904.97 Windows 10
游客
2019年11月18日 23:17

666

1
1 (@guest_1050) Chrome 78.0.3904.70 Windows 10
游客
2019年11月13日 10:21

666

aix
aix (@guest_990) Chrome 78.0.3904.97 Windows 10
游客
2019年11月10日 09:59

tttql

Demonnue
Demonnue (@guest_712) Chrome 68.0.3440.106 Windows 10
游客
2019年10月2日 19:42

zzwtql
zzwtql (@guest_544) Firefox 68.0 Windows 10
游客
2019年8月18日 13:52

您也太强

L
L (@guest_212) Chrome 69.0.3497.92 Windows 10
游客
2018年12月28日 21:07

感谢!

lala
lala (@guest_144) Chrome 70.0.3538.102 Mac OS X 10_14_0
游客
2018年11月27日 18:34

好人zzw

caiji
caiji (@guest_141) Chrome 70.0.3538.102 Windows 10
游客
2018年11月24日 11:39

tql