学校 | 华东师范大学 |
专业 | 计算机科学与技术 |
课程 | 嵌入式系统原理(实践课) |
教师 | 沈建华 |
年份 | 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