首先,我们看MSP430F5529的库函数里面,会发现有两个跟串口有关的头文件"eusci_a_uart.h"和"usci_a_uart.h"。我查阅了资料后发现,其实就是一个普通的串行通讯模块,一个是增强型串行通讯模块。然后寄存器不太一样。我看TI的例程里面用的是"usci_a_uart.h"里面的函数,我就也只讲这给里面的了。
GPIO_setAsPeripheralModuleFunctionInputPin()和GPIO_setAsPeripheralModuleFunctionOutputPin
大家一看,这个不是GPIO的函数吗?对的,不过你进行串口通讯需要用到他,我看手册里面说MSP430F5xx/6xx不具备这个功能,但是例程里面有,卡了我好久。我猜手册写错了。
函数声明
void GPIO_setAsPeripheralModuleFunctionInputPin (uint8_t selectedPort,uint16_t selectedPins ) void GPIO_setAsPeripheralModuleFunctionOutputPin (uint8_t selectedPort,uint16_t selectedPins )
作用
GPIO_setAsPeripheralModuleFunctionInputPin ()该函数在所选引脚的输入方向上配置外围模块函数。该函数为所选引脚的主、次或三元模块函数模式的输入方向配置外围模块函数。
GPIO_setAsPeripheralModuleFunctionOutputPin()该函数为所选引脚的输出方向配置外围模块函数。该函数在输出方向为所选引脚配置外围模块函数,用于主、次或三元模块函数模式。
这个是官方库函数手册翻译的结果,说实话我也看不懂,反正我们只需要用,知道用用法就行。
需要注意的一点就是,外设功能方向引脚看具体功能,有些外设不需要哦设置方向。例如uart,设置为引脚之后,系统会自动设置输入输出方向。所以我们直接选择GPIO_setAsPeripheralModuleFunctionInputPin ()这一个函数就可以了。
参数
selectedPort
//! - \b GPIO_PORT_P1 //! - \b GPIO_PORT_P2 //! - \b GPIO_PORT_P3 //! - \b GPIO_PORT_P4 //! - \b GPIO_PORT_P5 //! - \b GPIO_PORT_P6 //! - \b GPIO_PORT_P7 //! - \b GPIO_PORT_P8 //! - \b GPIO_PORT_P9 //! - \b GPIO_PORT_P10 //! - \b GPIO_PORT_P11 //! - \b GPIO_PORT_PA //! - \b GPIO_PORT_PB //! - \b GPIO_PORT_PC //! - \b GPIO_PORT_PD //! - \b GPIO_PORT_PE //! - \b GPIO_PORT_PF //! - \b GPIO_PORT_PJ
selectedPins
//! - \b GPIO_PIN0 //! - \b GPIO_PIN1 //! - \b GPIO_PIN2 //! - \b GPIO_PIN3 //! - \b GPIO_PIN4 //! - \b GPIO_PIN5 //! - \b GPIO_PIN6 //! - \b GPIO_PIN7 //! - \b GPIO_PIN8 //! - \b GPIO_PIN9 //! - \b GPIO_PIN10 //! - \b GPIO_PIN11 //! - \b GPIO_PIN12 //! - \b GPIO_PIN13 //! - \b GPIO_PIN14 //! - \b GPIO_PIN15 //! - \b GPIO_PIN_ALL8 //! - \b GPIO_PIN_ALL16
使用
MSP430F5529好像就两个串口,记住这下面两个就行,记不住收藏博客,需要用的时候过来抄。
需要注意,不需要我们像stm32那样配置3.4为输入,3.5为输出,直接像我下面这样写就可以了。
//Usart0 P3.4 = USCI_A0 RXD P3.3 = USCI_A0 TXD GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN3+GPIO_PIN4); //Usart1 P4.5 = USCI_A1 RXD P4.4 = USCI_A1 TXD GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN5+GPIO_PIN4);
USCI_A_UART_init()
函数声明
可能有人不知道bool是什么意思,bool就是布尔值,将人话就是0和非0。要么为真,要么为假,只有两种状态,这称之为布尔值。而非0就是1。
bool USCI_A_UART_init (uint16_t baseAddress,USCI_A_UART_initParam ∗ param )
作用
初始化串口
参数
baseAddress
说实话,TI这个老6库函数手册里面就一句话is the base address of the USCI_A_UART module。我尼玛怎么知道他们的地址。于是我找了一段时间,找到了以下参数,只有两个。
USCI_A0_BASE //串口0基地址 USCI_A1_BASE //串口1基地址
param
param是一个结构体,我娓娓道来。
(1)首先我先介绍 param1.selectClockSource,他是负责选择串口波特率发生时钟的。只有两个参数,ACLK= TACLK 32768Hz, MCLK= SMCLK= default DCO ~ 1048576Hz。
注意,对于较低的波特率(9600bps以下),可以选择ACLK作为时钟源(注意,是可以选择,不是只能选择)。在波特率高于9600bps的情况下,应选择频率较高的SMCLK作为时钟源。
USCI_A_UART_CLOCKSOURCE_SMCLK //1048576Hz USCI_A_UART_CLOCKSOURCE_ACLK //32768Hz
(2)然后是这4个,这四个负责配置串口波特率的,至于传入数值怎么算呢?点击这个网站,先根据我们之前设置的时钟,USCI/EUSCI选择USCI,然后波特率你自己选。下面这个就是SMCLK时钟频率下,波特率为9600的配置。
param1.clockPrescalar = 6; param1.firstModReg = 13; param1.secondModReg = 0; param1.overSampling = USCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION; #define USCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION 0x01 #define USCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION 0x00
(3)接下来这几个是选择,这三个都是基础。还有不清楚,你可以看一下你电脑端串口助手一般都有这几个要配置,你就按照我的来就可以了。然后下面这三个参数你也不用改,跟我的一样即可。
param1.parity = USCI_A_UART_NO_PARITY; //校验位,无 param1.msborLsbFirst = USCI_A_UART_LSB_FIRST; //数据低位先发 param1.numberofStopBits = USCI_A_UART_ONE_STOP_BIT; //一停止位
(4)最后一个参数 ,是用来配置串口模式的。就使用USCI_A_UART_MODE模式即可,其他几个我也不知道是做什么的。
param1.uartMode = USCI_A_UART_MODE;
详细解释,我们查看 USCI_A_UART_MODE定义发现还有三个其他的定义,而这四个定义本质如下
#define USCI_A_UART_MODE UCMODE_0 #define USCI_A_UART_IDLE_LINE_MULTI_PROCESSOR_MODE UCMODE_1 #define USCI_A_UART_ADDRESS_BIT_MULTI_PROCESSOR_MODE UCMODE_2 #define USCI_A_UART_AUTOMATIC_BAUDRATE_DETECTION_MODE UCMODE_3 #define UCMODE_0 (0x00) /* Sync. Mode: USCI Mode: 0 */ #define UCMODE_1 (0x02) /* Sync. Mode: USCI Mode: 1 */ #define UCMODE_2 (0x04) /* Sync. Mode: USCI Mode: 2 */ #define UCMODE_3 (0x06) /* Sync. Mode: USCI Mode: 3 */
我们查看USCI_A_UART_init()的内部函数实现,看到其实就算再操作UCAxCTL0这个寄存器,我们可以直接看手册。最后发现模式0,就是UART模式。模式1,空闲线路多处理器模式。模式2,地址位多处理器模式。模式3,自带动波特率检测UART模式。
注意,我们这个是UART模块,所以不能同步。
//Configure UART mode. HWREG8(baseAddress + OFS_UCAxCTL0) |= param->uartMode ;
USCI_A_UART_enable()
函数声明
void USCI_A_UART_enable (uint16_t baseAddress )
作用
使能UART。
参数
baseAddress
串口基地址,参数下面两个
USCI_A0_BASE //串口0基地址 USCI_A1_BASE //串口1基地址
使用
//Enable UART module for operation USCI_A_UART_enable(USCI_A0_BASE); //Enable UART module for operation USCI_A_UART_enable(USCI_A1_BASE);
USCI_A_UART_clearInterrupt()
函数声明
void USCI_A_UART_clearInterrupt (uint16_t baseAddress,uint8_t mask )
作用
清除UART中断标志位
参数
baseAddress
与上面一样,懒得再啰嗦
mask
就一个是接收中断,一个是发送中断。注意,我们发送数据一般是不需要中断,如果是发送一连串字符就需要了。但是接收数据一定要中断处理,这样能高效准确处理接收到的数据。
参数就两个
USCI_A_UART_RECEIVE_INTERRUPT_FLAG //接收中断标志位 USCI_A_UART_TRANSMIT_INTERRUPT_FLAG //发送中断标志位
使用
//Usart0 USCI_A_UART_clearInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT); //清除接收中断 USCI_A_UART_clearInterrupt(USCI_A0_BASE,USCI_A_UART_TRANSMIT_INTERRUPT); //清除发送中断 USCI_A_UART_clearInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT + USCI_A_UART_TRANSMIT_INTERRUPT); //清除接收和发送中断 //Usart1 USCI_A_UART_clearInterrupt(USCI_A1_BASE,USCI_A_UART_RECEIVE_INTERRUPT); //清除接收中断 USCI_A_UART_clearInterrupt(USCI_A1_BASE,USCI_A_UART_TRANSMIT_INTERRUPT); //清除发送中断 USCI_A_UART_clearInterrupt(USCI_A1_BASE,USCI_A_UART_RECEIVE_INTERRUPT + USCI_A_UART_TRANSMIT_INTERRUPT); //清除接收和发送中断
USCI_A_UART_enableInterrupt()
与USCI_A_UART_clearInterrupt参数与使用一摸一样。
作用是使能串口中断,使用方法如下
//Usart0 USCI_A_UART_enableInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT); //使能接收中断 USCI_A_UART_enableInterrupt(USCI_A0_BASE,USCI_A_UART_TRANSMIT_INTERRUPT); //使能发送中断 USCI_A_UART_enableInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT + USCI_A_UART_TRANSMIT_INTERRUPT); //使能接收和发送中断 //Usart1 USCI_A_UART_enableInterrupt(USCI_A1_BASE,USCI_A_UART_RECEIVE_INTERRUPT); //使能接收中断 USCI_A_UART_enableInterrupt(USCI_A1_BASE,USCI_A_UART_TRANSMIT_INTERRUPT); //使能发送中断 USCI_A_UART_enableInterrupt(USCI_A1_BASE,USCI_A_UART_RECEIVE_INTERRUPT + USCI_A_UART_TRANSMIT_INTERRUPT); //使能接收和发送中断
串口中断函数
中断函数中的switch
因为MSP430F5529的中断,是多个中断标志位公用同一个中断向量,所以我们需要加上Switch语句。如下
串口中断服务函数框架
串口中断服务函数框架如下
注意,我们只需要在case 2:里面增加或者减少内容即可。
//UART0串口中断服务函数 #pragma vector=USCI_A0_VECTOR __interrupt void USCI_A0_ISR (void) { uint8_t receivedData = 0; switch (__even_in_range(UCA0IV,4)) { //Vector 2 - RXIFG case 2: receivedData = USCI_A_UART_receiveData(USCI_A0_BASE); USCI_A_UART_transmitData(USCI_A0_BASE,receivedData); break; default: break; } } //UART1串口中断服务函数 #pragma vector=USCI_A1_VECTOR __interrupt void USCI_A1_ISR (void) { uint8_t receivedData = 0; switch (__even_in_range(UCA1IV,4)) { //Vector 2 - RXIFG case 2: receivedData = USCI_A_UART_receiveData(USCI_A1_BASE); USCI_A_UART_transmitData(USCI_A1_BASE,receivedData); break; default: break; } }
printf函数支持
printf函数
拜读了大佬的博客。MSP430F5529的重定向fputc(int ch, FILE *f)直接使用printf的方法只有字符串和%s打印正常,数字打印不出来。所以采用下面这个
//这三个头文件都要加上,不然会报错 #include <string.h> #include <stdarg.h> #include <stdio.h> void UART_printf(uint16_t baseAddress, const char *format,...) { uint32_t length; va_list args; uint32_t i; char TxBuffer[128] = {0}; va_start(args, format); length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer), (char*)format, args); va_end(args); for(i = 0; i < length; i++) USCI_A_UART_transmitData(baseAddress, TxBuffer[i]); }
使用
函数使用方法很简单,和printf函数几乎一样。唯一区别是,此处的UART_printf需要提前告知是串口0还是串口1。
//串口0发送 UART_printf(USCI_A0_BASE, "数字测试:%d,字符串测试:%s\r\n", 2333, "能收到就算成功"); //串口1发送 UART_printf(USCI_A1_BASE, "数字测试:%d,字符串测试:%s\r\n", 2333, "能收到就算成功");
浮点数据打印
如果你不设置工程编译环境,那么你可能打印不出来浮点型数据。步骤如下:
右键工程文件——>properties
然后是build——>Advanced Options
实验
以下就是串口0将接收到的数据发送出去。同时每个1s给上位机发送一串字符。
#include "driverlib.h" #include <string.h> #include <stdarg.h> #include <stdio.h> #define CPU_F ((double)1000000) #define delay_us(x) __delay_cycles((long)(CPU_F*(double)x/1000000.0)) #define delay_ms(x) __delay_cycles((long)(CPU_F*(double)x/1000.0)) void UART_printf(uint16_t baseAddress, const char *format,...) { uint32_t length; va_list args; uint32_t i; char TxBuffer[128] = {0}; va_start(args, format); length = vsnprintf((char*)TxBuffer, sizeof(TxBuffer), (char*)format, args); va_end(args); for(i = 0; i < length; i++) USCI_A_UART_transmitData(baseAddress, TxBuffer[i]); } //9600 void Usart1_Init() { //P4.4=UCA1TXD P4.5=UCA1RXD GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P4, GPIO_PIN5+GPIO_PIN4); USCI_A_UART_initParam param1 = {0}; param1.selectClockSource = USCI_A_UART_CLOCKSOURCE_SMCLK; param1.clockPrescalar = 6; param1.firstModReg = 13; param1.secondModReg = 0; param1.parity = USCI_A_UART_NO_PARITY; //无校验位 param1.msborLsbFirst = USCI_A_UART_LSB_FIRST; //低位先行 param1.numberofStopBits = USCI_A_UART_ONE_STOP_BIT; //1停止位 param1.uartMode = USCI_A_UART_MODE; param1.overSampling = USCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION; if (STATUS_FAIL == USCI_A_UART_init(USCI_A1_BASE, ¶m1)){ return; } //Enable UART module for operation USCI_A_UART_enable(USCI_A1_BASE); //Enable Receive Interrupt USCI_A_UART_clearInterrupt(USCI_A1_BASE,USCI_A_UART_RECEIVE_INTERRUPT); USCI_A_UART_enableInterrupt(USCI_A1_BASE,USCI_A_UART_RECEIVE_INTERRUPT); } void Usart0_Init(void) { GPIO_setAsPeripheralModuleFunctionInputPin(GPIO_PORT_P3, GPIO_PIN3+GPIO_PIN4); USCI_A_UART_initParam param1 = {0}; param1.selectClockSource = USCI_A_UART_CLOCKSOURCE_SMCLK; param1.clockPrescalar = 6; param1.firstModReg = 13; param1.secondModReg = 0; param1.overSampling = USCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION; param1.parity = USCI_A_UART_NO_PARITY; param1.msborLsbFirst = USCI_A_UART_LSB_FIRST; param1.numberofStopBits = USCI_A_UART_ONE_STOP_BIT; param1.uartMode = USCI_A_UART_MODE; if (STATUS_FAIL == USCI_A_UART_init(USCI_A0_BASE, ¶m1)){ return; } //Enable UART module for operation USCI_A_UART_enable(USCI_A0_BASE); //Enable Receive Interrupt USCI_A_UART_clearInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT); USCI_A_UART_enableInterrupt(USCI_A0_BASE,USCI_A_UART_RECEIVE_INTERRUPT); } void main(void) { WDT_A_hold(WDT_A_BASE); Usart0_Init(); //interrupts enabled __bis_SR_register(GIE); while(1) { UART_printf(USCI_A0_BASE, "数字测试:%d,字符串测试:%s\r\n", 2333, "能收到就算成功"); delay_ms(1000); } } #pragma vector=USCI_A0_VECTOR __interrupt void USCI_A0_ISR (void) { uint8_t receivedData = 0; switch (__even_in_range(UCA0IV,4)) { //Vector 2 - RXIFG case 2: receivedData = USCI_A_UART_receiveData(USCI_A0_BASE); USCI_A_UART_transmitData(USCI_A0_BASE,receivedData); break; default: break; } } #pragma vector=USCI_A1_VECTOR __interrupt void USCI_A1_ISR (void) { uint8_t receivedData = 0; switch (__even_in_range(UCA1IV,4)) { //Vector 2 - RXIFG case 2: receivedData = USCI_A_UART_receiveData(USCI_A1_BASE); USCI_A_UART_transmitData(USCI_A1_BASE,receivedData); break; default: break; } }
串口1所在位置
可能有些人发现P4.4和P4.5位置找不到,其实是在开发板这个地方。这里有跳线帽连着,需要把跳线帽摘下来,然后连接上USB转串口的模块。