一个人的角落,也许并没人会看到这里~~ 祝福你,工作顺心,学习快乐~

MSP432

MSP432 Graphics Library

MSP432提供了对LCD屏操作的库函数,而从整个对LCD的操控来说,自下面上的分成了三层:HAL硬件抽象层,LCD屏的驱动层以及grlib上层图形库。

按照官方文档的介绍grlib库提供了一些上下文控制函数以及一系列简单的图表绘制函数:圆,位图,直线,矩形,字符串,按钮,图像按钮,单选框,多选框,以及位图数据转C格式数据工具。

HAL实现对MCU与LCD之间接口的配置,以及底层LCD控制器的串行通信,实现控制字符与数据字符的传输。 LCD驱动层利用HAL实现LCD的基本元素的绘制(点、直线等的绘制),为 grlib 提供调用接口。

以下为三层架构所对应的头文件,包含各自了所在层的函数原型,以及函数调用的方法

#include <ti/grlib/grlib.h>
#include "LcdDriver/Crystalfontz128x128_ST7735.h"
#include "LcdDriver/HAL_MSP_EXP432P401R_Crystalfontz128x128_ST7735.h"

LCD的初始化代码一般如下:

    /* Graphic library context */
    Graphics_Context g_sContext;  //全局上下文

    /* Initializes display */
    Crystalfontz128x128_Init();   //驱动层的LCD初始化代码

    /* Set default screen orientation */
    Crystalfontz128x128_SetOrientation(0);  //设置屏幕方向

    /* Initializes graphics context */
    Graphics_initContext(&g_sContext, &g_sCrystalfontz128x128, &g_sCrystalfontz128x128_funcs); 
    Graphics_setForegroundColor(&g_sContext, GRAPHICS_COLOR_BLUE);
    Graphics_setBackgroundColor(&g_sContext, GRAPHICS_COLOR_WHITE);
    GrContextFontSet(&g_sContext, &g_sFontFixed6x8);

可以看出初始化函数调用关系是逐层的,驱动层初始化通过调用HAL层进行硬件初始化,其中包括PortInit、SpiInit与LCD芯片初始化。 PortInit:为TI通用的端口复用配置,针对不同TI芯片电路进行端口的开启与复用,此部分代码同样为通用。

需要定制的只有HAL通信功能引脚的配置:

// Ports from MSP432 connected to LCD
#define LCD_SCK_PORT          GPIO_PORT_P1
#define LCD_SCK_PIN_FUNCTION  GPIO_PRIMARY_MODULE_FUNCTION
#define LCD_MOSI_PORT         GPIO_PORT_P1
#define LCD_MOSI_PIN_FUNCTION GPIO_PRIMARY_MODULE_FUNCTION
#define LCD_RST_PORT          GPIO_PORT_P5
#define LCD_CS_PORT           GPIO_PORT_P5
#define LCD_DC_PORT           GPIO_PORT_P3

// Pins from MSP432 connected to LCD
#define LCD_SCK_PIN           GPIO_PIN5
#define LCD_MOSI_PIN          GPIO_PIN6
#define LCD_RST_PIN           GPIO_PIN7
#define LCD_CS_PIN            GPIO_PIN0
#define LCD_DC_PIN            GPIO_PIN7

// Definition of USCI base address to be used for SPI communication
#define LCD_EUSCI_BASE        EUSCI_B0_BASE

grlib的初始化内容则是主要围绕一个g_sContext结构体进行, Graphics_Context 结构体定义如下:

typedef struct Graphics_Context
{
    int32_t size;                         //!< The size of this structure.
    const Graphics_Display *display;    //!< The screen onto which drawing operations are performed.
    Graphics_Rectangle clipRegion;        //!< The clipping region to be used when drawing onto the screen.
    uint32_t foreground;                 //!< The color used to draw primitives onto the screen.
    uint32_t background;                 //!< The background color used to draw primitives onto the screen.
    const Graphics_Font *font;            //!< The font used to render text onto the screen.
} Graphics_Context;

Graphics_Display *display结构体包括了特定LCD的宽高信息以及驱动函数指针。由此可以看出,LCD屏与上grlib也是分离的,当硬件更换为不同的LCD时,只需修改对应LCD驱动层函数指针,甚至可以实现LCD热更换。

但三层架构的设计架构移植性较高,牺牲了一部分屏幕控制器的硬件性能。

MSP432 launchpad User Application Port UART

串口通信的配置

串口的配置 主要包括两个方面:

  • 初始化结构体的配置
  • 串口模块中断的开启

以下是基于DriverLib的初始化代码

    const eUSCI_UART_Config uartConfig =
    {
            EUSCI_A_UART_CLOCKSOURCE_SMCLK,        // SMCLK Clock Source 48Mhz
            312,                                   // BRDIV = 312
            8,                                     // UCxBRF = 8
            0xAA,                                  // UCxBRS = 0xAA
            EUSCI_A_UART_NO_PARITY,                // No Parity
            EUSCI_A_UART_LSB_FIRST,                // MSB First
            EUSCI_A_UART_ONE_STOP_BIT,             // One stop bit
            EUSCI_A_UART_MODE,                     // UART mode
            EUSCI_A_UART_OVERSAMPLING_BAUDRATE_GENERATION  // overSampling Mode
    };

    /* Configuring UART Module */
     UART_initModule(EUSCI_A0_BASE, &uartConfig);

    /* Enable UART module */
     UART_enableModule(EUSCI_A0_BASE);

     UART_enableInterrupt(EUSCI_A0_BASE, EUSCI_A_UART_RECEIVE_INTERRUPT);
     Interrupt_enableInterrupt(INT_EUSCIA0);

     MAP_Interrupt_enableSleepOnIsrExit(); //低功耗模式

比较困难的地方在于波特率的设置主要涉及到BRDIV,UCxBRF,UCxBRS, overSampling Mode这几个寄存器参数。

DriverLib手册中提到了一个参数计算器 http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html

如果上位机可以接收到FF FE之类的乱码一般是波特率设置不正确导致,不同的时钟源频率需要将其分离成所需要的频率,具体的计算公式在Reference Manual 的22.3.10小节有详细说明,将计算出来的波特率只要与上位机设置的相差不远也是可以进行通信的。

此外,网上有一部分相关帖子提到中断名的问题在CCS里边,系统会为所有中断定义一个默认中断,如果程序没有定义相关中断,在程序中又开启了此中断,中断触发后将会陷入到默认中断中。这里的默认中断内容是一个死循环,中断向量表在startup_msp432p401r_ccs.c中有定义。

  //Configure UART pins

  P1SEL0 |=BIT2 | BIT3;                  // set2-UART pin as second function



 __enable_interrupt();

 NVIC->ISER[0] = 1 << ((EUSCIA0_IRQn) & 31); // EnableeUSCIA0 interrupt in NVIC module

  //Configure UART

  UCA0CTLW0|= UCSWRST;
  UCA0CTLW0|= UCSSEL__SMCLK;             // PuteUSCI in reset

  // BaudRate calculation
  //12000000/(16*9600) = 78.125
  //Fractional portion = 0.125
  // User'sGuide Table 21-4: UCBRSx = 0x10
  // UCBRFx =int ( (78.125-78)*16) = 2

  UCA0BR0 =78;                           //12000000/16/9600
  UCA0BR1 =0x00;
  UCA0MCTLW= 0x1000 | UCOS16 | 0x0020;    //注意这一行的设定

  UCA0CTLW0&= ~UCSWRST;                  //Initialize eUSCI

  UCA0IE |=UCRXIE;                       // EnableUSCI_A0 RX interrupt

近几年来使用DriverLib开发的越来越多了,个人认为DriverLib可以在一定程度上简化开发流程,对新手友好,上手快,但是由于432官方对DriverLib手册并不是非常的友好,很多函数仅仅是一带而过,函数说明甚至没有源码注释写得好,这一使得学习432的内容需要齐头并进,能够简单易懂的用DriverLib难一些的还是要参考寄存器配置。

以下是串口中断处理函数,利用寄存器操作,将收到的串口数据进行回传。

/*
 * EUSCI A0 UART interrupt handler.
 */
void EUSCIA0_IRQHandler(void)
{
    if(UCA0IFG & UCRXIFG)
    {
     while(!(UCA0IFG&UCTXIFG));
           UCA0TXBUF = UCA0RXBUF;
     __no_operation();

    }
}

而DriverLib版:

    int receiveByte;
    receiveByte = UART_receiveData(EUSCI_A0_BASE);
    UART_transmitData(EUSCI_A0_BASE,receiveByte);

DriverLib的底层实现也是通过寄存器来完成的,这样可以通过学习源代码的方式了解某些寄存器的功能,省略某些技术细节,随着处理器执行效率的提高,代码越来越复杂,这样的库函数操作将会非常常见。

MSP432 使用Timer_A输出PWM

Timer_A 输出 PWM的问题

在官方例程中给出的是一种直接操作寄存器的方式启动的,这让我们这些刚刚接触MSP432这种嵌入式芯片的来说是很难看懂的,然而PWM的输出是可以重新映射到其它端口输出的,例程中又掺杂了PMAP GPIO端口的映射,这更使得学习无从下手。

通过寄存器操作的方式输出PWM波形

    TIMER_A0->CCR[0] = PWM_PERIOD;
    TIMER_A0->CCTL[1] = TIMER_A_CCTLN_OUTMOD_7;      // CCR1 Reset/set
    TIMER_A0->CCR[1] = PWM_PERIOD * (55 / 100.0);    // CCR1 PWM duty cycle
    TIMER_A0->CTL = TIMER_A_CTL_SSEL__SMCLK // 使用SMCLK时钟源
                  | TIMER_A_CTL_MC__UP   
                  | TIMER_A_CTL_CLR;  // SMCLK, up mode, clear TAR

引用msp432p401r.h头文件定义:

#define TIMER_A_CTL_MC__UP    ((uint16_t)0x0010)    /*!< Up mode: Timer counts up to TAxCCR0 */

通过 动态修改 TIMER_A0->CCR[1] 内容可以改变PWM间隔时间

通过DriverLib操作的方式输出PWM波形

/* Timer_A PWM Configuration Parameter */
Timer_A_PWMConfig pwmConfig =
{
    TIMER_A_CLOCKSOURCE_SMCLK,
    TIMER_A_CLOCKSOURCE_DIVIDER_1,
    32000,
    TIMER_A_CAPTURECOMPARE_REGISTER_1,
    TIMER_A_OUTPUTMODE_RESET_SET,
    3200
};
Timer_A_generatePWM(TIMER_A1_BASE, &pwmConfig);

Timer_A_setCompareValue(TIMER_A2_BASE, TIMER_A_CAPTURECOMPARE_REGISTER_1, PWM_PERIOD * (50/100.0f)); //动态设置PWM 间隔时间

而timer_a.C文件中对Timer_A_generatePWM函数定义如下

void Timer_A_generatePWM(uint32_t timer, const Timer_A_PWMConfig *config)
{
    //此处省略一部分,参数合法判断...

    privateTimer_AProcessClockSourceDivider(timer, config->clockSourceDivider);

    TIMER_A_CMSIS(timer)->CTL &=
            ~(TIMER_A_CLOCKSOURCE_INVERTED_EXTERNAL_TXCLK + TIMER_A_UPDOWN_MODE
                    + TIMER_A_DO_CLEAR + TIMER_A_TAIE_INTERRUPT_ENABLE);

    TIMER_A_CMSIS(timer)->CTL |= (config->clockSource + TIMER_A_UP_MODE
            + TIMER_A_DO_CLEAR);

    TIMER_A_CMSIS(timer)->CCR[0] = config->timerPeriod;

    TIMER_A_CMSIS(timer)->CCTL[0] &= ~(TIMER_A_CAPTURECOMPARE_INTERRUPT_ENABLE
            + TIMER_A_OUTPUTMODE_RESET_SET);

    uint8_t idx = (config->compareRegister>>1) - 1;
    TIMER_A_CMSIS(timer)->CCTL[idx] |= config->compareOutputMode;
    TIMER_A_CMSIS(timer)->CCR[idx] = config->dutyCycle;
}

可以看到相关操作与寄存器类似,但一次只能修改一路PWM输出。难怪官方例程中直接使用寄存器操作,多路输出更加容易控制。

MSP432中GPIO中断相关操作

GPIO的P1,P2,PA支持中断

MSP432 中的中断相关寄存器 PxIFG 中断标志寄存器 PxIV 中断向量寄存器

Driverlib 所涉及到GPIO中断的函数

GPIO_registerInterrupt GPIO_unregisterInterrupt //Registers/UnRegister s an interrupt handler for the port interrupt. GPIO_disableInterrupt GPIO_enableInterrupt GPIO_getEnabledInterruptStatus GPIO_interruptEdgeselect GPIO_getInterruptStatus GPIO_clearInterruptFlag

![GPIO_IES寄存器][/post_res/GPIO_IES.png]

![GPIO_PxIFG寄存器][/post_res/GPIO_PxIFG.png]

![GPIO_PxIV寄存器][/post_res/GPIO_PxIV.png]

对中断的配置

/* Confinguring P1.1 & P1.4 as an input and enabling interrupts */
MAP_GPIO_setAsInputPinWithPullUpResistor(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4);
MAP_GPIO_clearInterruptFlag(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4);
MAP_GPIO_enableInterrupt(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4);
MAP_GPIO_interruptEdgeSelect(GPIO_PORT_P1, GPIO_PIN1 | GPIO_PIN4, GPIO_HIGH_TO_LOW_TRANSITION);