
| 学校 | 华东师范大学 |
| 专业 | 计算机科学与技术 |
| 课程 | 嵌入式系统原理(实践课) |
| 教师 | 沈建华 |
| 年份 | 2018年秋 |
在调试机的超级终端上输入自己学号的后两位,并以空格键结束(下面以尾号为87为例),通过串口将该这两位数传送至MCU,再由MCU触发LED顺序显示这2位数,即先显示8再显示7(数值0显示10下)。显示的方法是以3Hz频率的闪烁,如数值8表现为LED闪烁8下。在两位数显示间熄灭LED灯1S。串口每发送一次字符,显示1次数据,不循环显示。(注:要求LED闪烁及跳变间隔控制需由硬件定时器控制)
非常简单,直接给出代码。
#include <ti/devices/msp432p4xx/driverlib/driverlib.h>
/* Standard Includes */
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#define CLK 1000000
// 时钟取1M
#define FREQ 3
// 3Hz频率
#define MAXLEN 10
// 学号的长度
char num[MAXLEN + 2]={0}; // num数组存放接收到的值,LEN表示学号的长度,这里是2,另外还有一个空格(表示结束)和一个'\0'(以方便将char*当作%s使用),所以num的长度是LEN+2
int cursor = 0; // 光标,记录下一个要接收到的字符要写入的num数组的哪个位置
int idx = 0; // 索引,表示当前在处理学号的第几位
int cnt = 0; // 计数,表示需要亮多少次,这里会对字符num[idx]先减去'0'得到数值,然后乘2,因为一次闪烁有亮和灭2个操作
int op = 1; // 表示现在是要开灯还是关灯
int len = 0;
/*
http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
*/
const eUSCI_UART_Config uartConfig =
{
EUSCI_A_UART_CLOCKSOURCE_SMCLK, // 选用SMCLK(1M)时钟源
6, // BRDIV = 6 ,clockPrescalar时钟分频系数
8, // UCxBRF = 8 firstModReg (BRDIV、UCxBRF、 UCxBRS和SMCLK,用于设置串口波特率)
17, // UCxBRS = 17 secondModReg
EUSCI_A_UART_NO_PARITY, // 校验位None
EUSCI_A_UART_LSB_FIRST, // 低位优先,小端模式
EUSCI_A_UART_ONE_STOP_BIT, // 停止位1位
EUSCI_A_UART_MODE, // UART mode
1 // 设置为过采样,该数值为1
};
int main(void)
{
/* 停用开门狗 */
MAP_WDT_A_holdTimer();
//![Simple FPU Config]
MAP_FPU_enableModule(); /* 启用FPU加快DCO频率计算,注:DCO是内部数字控制振荡器,默认是3M频率 */
MAP_CS_setDCOFrequency(CLK); /* 设置DCO频率为指定频率,此处DCO=1M */
MAP_CS_initClockSignal(CS_MCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1); /* 设置MCLK(主时钟,可用于CPU主频等),MCLK=DCO频率/时钟分频系数,此处MCLK=DCO=1M */
MAP_CS_initClockSignal(CS_HSMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1); /* 设置HSMCLK(子系统主时钟),HSMCLK=DCO频率/时钟分频系数,此处HSMCLK=DCO/1=1M */
MAP_CS_initClockSignal(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1); /* 设置SMCLK(低速子系统主时钟,可用TimerA频率),SMCLK=DCO频率/时钟分频系数,此处SMCLK=DCO/1=1M */
//![Simple FPU Config]
/* 选用P1.2和P1.3使用UART模式 */
MAP_GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P1,GPIO_PIN2 | GPIO_PIN3, GPIO_PRIMARY_MODULE_FUNCTION);
/* 使用uartConfig配置UART_A0 */
MAP_UART_initModule(EUSCI_A0_BASE, &uartConfig);
/* 使能UART_A0 */
MAP_UART_enableModule(EUSCI_A0_BASE);
MAP_UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT); /* 使能UART_A0串口接收中断 */
MAP_Interrupt_enableInterrupt(INT_EUSCIA0); /* 使能UART_A0中断 */
// 定时器频率 = MCLK / 定时器分频系数
MAP_Timer32_initModule(TIMER32_0_BASE, TIMER32_PRESCALER_1, TIMER32_32BIT,TIMER32_PERIODIC_MODE);
MAP_Interrupt_enableInterrupt(INT_T32_INT1);
MAP_Timer32_enableInterrupt(TIMER32_0_BASE);
// TIMER32_1_BASE INT_T32_INT2
MAP_Timer32_startTimer(TIMER32_0_BASE, false);
MAP_Interrupt_enableMaster(); /*使能中断总开关*/
// LED2.1作为输出灯,默认熄灭
GPIO_setAsOutputPin(GPIO_PORT_P2,GPIO_PIN0);
GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN0);
while(1);
}
/*UART_A0中断函数*/
void EUSCIA0_IRQHandler(void)
{
uint32_t status = MAP_UART_getEnabledInterruptStatus(EUSCI_A0_BASE); /* 将UART_A0中断标志位的值赋值给status */
MAP_UART_clearInterruptFlag(EUSCI_A0_BASE, status); /* 清空UART_A0中断标志位 */
if(status & EUSCI_A_UART_RECEIVE_INTERRUPT_FLAG) {
/* 判断是否为UART_A0接受中断 */
char c = MAP_UART_receiveData(EUSCI_A0_BASE);
if (c == ' ') {
len = cursor;
idx = 0; // 如果收到了空格,说明当前的数据已经接收完毕(以空格键结束),那么接下来就是要闪烁了,所以处理第idx=0位
cnt = num[idx] - '0'; // 是字符,减去ord('0'),得到数值
// if (cnt == 0) cnt = 10; // 如果学号某一位是0,那么'0'-'0'=0,需要闪烁10次,所以这里设置为10
cnt *=2; // 由于下面是把亮、灭看作2个操作,所以闪烁cnt次就有cnt*2次操作
MAP_Timer32_setCount(TIMER32_0_BASE, CLK / FREQ / 2); // 1M除以3Hz是一次亮灭的时间,如果把亮和灭看作2个操作则还要除以2
} else {
// 如果不是空格,那么就把当前接收到的char c存入num数组的cursor指向的位置
num[cursor++] = c;
// MAP_UART_transmitData(EUSCI_A0_BASE, c); // 如果必要(方便调试),可以将得到的信息回显,重新输出到UART
}
}
}
/*计时器中断函数*/
void T32_INT1_IRQHandler() {
MAP_Timer32_clearInterruptFlag(TIMER32_0_BASE);
if (cnt != 0){
// 如果cnt不为0,说明还有剩下的操作,说明还要继续闪烁,则执行
// MAP_UART_transmitData(EUSCI_A0_BASE, cnt+'0'); // 如果必要(方便调试),可以输出看看当前做到第几步了
if (op)
GPIO_setOutputHighOnPin(GPIO_PORT_P2,GPIO_PIN0);
else
GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN0);
op = 1 - op; // 根据op是1还是0决定是开灯还是关灯,并将op的值更新为下一次的操作,用op = 1 - op
cnt--; // 执行了一次操作了
MAP_Timer32_setCount(TIMER32_0_BASE, CLK / FREQ / 2); // 3Hz频率
} else {
// cnt为0,说明当前这一位学号已经闪烁完毕了,下面首先熄灭1秒
GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN0);
MAP_Timer32_setCount(TIMER32_0_BASE, CLK);
idx++; // 然后要对下一位学号进行闪烁
if (idx < len){
// 如果当前处理的学号位数没有超过总长度,那么,继续执行下一个学号
cnt = num[idx] - '0';
// if (cnt == 0) cnt = 10;
cnt *= 2; // 与之前的操作类似
} else {
cursor = 0; // 如果idx超过了LEN,说明所有学号都闪烁完毕了,这时候,idx > LEN,将不执行闪烁
// 但是重置 cursor = 0,这样下一次 UART 收到消息的时候,就是从头开始填入了
}
}
}
/*
2个特性(我觉得可以给我加分,逃):
一,
可以多次接收值。
最后一位学号处理完,会进入 idx > LEN,如果画成有限状态机,那么不执行任何操作,自旋等待,当计时器触发时,发现不满足任何 if 条件,不执行,在中断绕一圈就回去。
直到重新获得值,在UART中断保存值,并直到空格,重置 idx = 0。
二,
甚至可以接收3个长度的学号,例如输入 154[空格],那么闪烁1下、间隔1秒、闪烁5下、间隔1秒、闪烁4下,当然更长的长度也可以。
原理很简单,len = cursor,然后用 len 控制有限状态机。
*/

tttttql
tql