MSP430F5529LP SPI 通信测试
这次来测测SPI通信功能,最近买了个MAX7219驱动的LED点阵,恰好也是SPI通信,可以拿来测测。
SPI 通信
首先来看看开发板上引出的SPI接口。

其中完整的SPI接口是P3.0, P3.1 和 P3.2, 分别对应MOSI, MISO,CLK。 MAX7219 作为从设备,只需要接收控制信号,不需要输出数据到板子,所以使用三线制SPI,CS线可以是普通IO,为此我选择的三线SPI是:
P3.0 MOSI
P3.2 CLK
P6.4 CS/LOAD

USCI SPI 模式
UCSI 的 SPI 模式,其控制逻辑如下图所示,里面包含了时钟控制,波特率设定,数据收发,中断等所需的所有寄存器。后续使用寄存器编程可以参考此图。

MAX7219 LED 驱动器
接下来看看这个LED点阵屏啥样,8*8的LED单色点阵屏,下面带了个MAX7219 LED驱动器。除了可以驱动点阵屏,还可以驱动7段译码管。单位需要多本通信工程师职称证书挂资质,欢迎联系,陈工15007599549微同

这个点阵屏是可以级联的,通过SPI通信,引出5个线,3根控制线,剩下VCC,GND两根线. 对应板子的连线如下:
VCC --- 5V
GND --- GND
DIN --- P3.0
CS --- P6.4
CLK --- P3.2
驱动器包含一些8位寄存器,可以控制驱动器的输出,从而显示想要的点阵。

Digit0 - Digit7 对应8行的灯的输出信号,一个寄存器对应8个灯,上面表格的最右侧是寄存器的地址。给MAX7219 发送数据时,先发送寄存器地址,再发送数据。
Decode Mode 设置译码模式
Intensity 设置亮度等级(0x00 - 0x0F)
Scan limit 设置扫描限制,设置后可以只扫描一部分灯,对于点阵屏自然是全部扫描,所以设置为0x07 (参考手册)
Shutdown 使能功能,启用则可以点亮LED
Display Test 没试过,应该是用于测试,类似自检
测试
下面来测试下。代码如下,直接看注释,比较简单。
#include <msp430.h> #include <stdint.h> void spi_send(uint8_t address, uint8_t data);void init_MAX7219(void);// MAX7219 Register addresses #define MAX_NOOP 0x00 #define MAX_DIGIT0 0x01 #define MAX_DIGIT1 0x02 #define MAX_DIGIT2 0x03 #define MAX_DIGIT3 0x04 #define MAX_DIGIT4 0x05 #define MAX_DIGIT5 0x06 #define MAX_DIGIT6 0x07 #define MAX_DIGIT7 0x08 #define MAX_DECODEMODE 0x09 #define MAX_INTENSITY 0x0A #define MAX_SCANLIMIT 0x0B #define MAX_SHUTDOWN 0x0C #define MAX_DISPLAYTEST 0x0F // 8x8 number font const uint8_t number[][8] = { {0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c}, // 0 {0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c}, // 1 {0x3c, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x7e}, // 2 {0x3c, 0x66, 0x06, 0x1e, 0x1e, 0x06, 0x66, 0x3c}, // 3 {0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00}, // heart-full // {0x66, 0xDB, 0x81, 0x81, 0x42, 0x24, 0x18, 0x00}, // heart-empty // {0x00, 0x24, 0x7E, 0x7E, 0x3C, 0x18, 0x00, 0x00}, // small heart {0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18}, // heart-full };const uint8_t iloveu[][8] = { {0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E}, // I {0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18}, // love, heart-full {0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18}, // U };const uint8_t create_heart[10][2] = { // row, value {0, 0x00}, {2, 0x18}, {1, 0x24}, {1, 0x66}, {2, 0x99}, {3, 0x81}, {4, 0x81}, {5, 0x42}, {6, 0x24}, {7, 0x18},};uint8_t returnValue_SPI = 0x00;uint8_t row, framecounter;void clear_screen() { int row; for (row = 0; row < 8; row++) { spi_send(MAX_DIGIT0 + row, 0x00); } __delay_cycles(500000); // Wait a bit before showing next frame }int main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P6OUT |= BIT4; // Set P6.4 for CS P6DIR |= BIT4; // Set P6.4 to output direction P3SEL |= BIT0 + BIT2; // P3.0,2 option select //P3DIR |= BIT0+BIT1+BIT2; // Set P3.0,2 to output direction UCB0CTL1 |= UCSWRST; // **Put state machine in reset** UCB0CTL0 |= UCMST + UCCKPH + UCMSB + UCSYNC; // 3-pin, 8-bit SPI master // MSB UCB0CTL1 |= UCSSEL_2; // SMCLK UCB0BR0 = 0x02; // /2 UCB0BR1 = 0; // UCB0CTL1 &= ~UCSWRST; // Initialize USCI state machine __delay_cycles(100000); init_MAX7219(); __delay_cycles(1000); uint8_t row, framecounter; for (framecounter = 3; framecounter > 0; framecounter--) { // Load all 8 row/digit registers with data from number[framecounter] for (row = 0; row < 8; row++) { spi_send(MAX_DIGIT0 + row, number[framecounter][row]); } __delay_cycles(500000); // Wait a bit before showing next frame clear_screen(); } for (framecounter = 0; framecounter < 3; framecounter++) { // Load all 8 row/digit registers with data from number[framecounter] for (row = 0; row < 8; row++) { spi_send(MAX_DIGIT0 + row, iloveu[framecounter][row]); } __delay_cycles(500000); // Wait a bit before showing next frame clear_screen(); } for (framecounter = 0; framecounter < 10; framecounter++) { spi_send(MAX_DIGIT0 + create_heart[framecounter][0], create_heart[framecounter][1]); __delay_cycles(200000); // Wait a bit before showing next frame } while (1) { // Loop through some frames for (framecounter = 4; framecounter < 6; framecounter++) { // Load all 8 row/digit registers with data from number[framecounter] if (framecounter == 4) spi_send(MAX_INTENSITY, 0x07); else spi_send(MAX_INTENSITY, 0x02); for (row = 0; row < 8; row++) { spi_send(MAX_DIGIT0 + row, number[framecounter][row]); } __delay_cycles(400000); // Wait a bit before showing next frame } }}// Send 16 bits as: xxxxaaaadddddddd (ignore, address, data) // and use active low Chip Select void spi_send(uint8_t address, uint8_t data){ P6OUT &= ~BIT4; // CS low _delay_cycles(50); while (!(UCB0IFG & UCTXIFG)){} // Wait until done UCB0TXBUF = (address & 0x0F); while (!(UCB0IFG & UCTXIFG)){} // Wait until done UCB0TXBUF = (data); while (!(UCB0IFG & UCTXIFG)){} // Wait until done P6OUT |= BIT4; // CS high }void init_MAX7219(void){ // Initialise MAX7219 with 8x8 led matrix spi_send(MAX_NOOP, 0x00); // NO OP (seems needed after power on) spi_send(MAX_SCANLIMIT, 0x07); // Enable all digits (always needed for current/8 per row) spi_send(MAX_INTENSITY, 0x03); // Display intensity (0x00 to 0x0F) spi_send(MAX_DECODEMODE, 0); // No BCD decoding for led matrix // Clear all rows/digits spi_send(MAX_DIGIT0, 0); spi_send(MAX_DIGIT1, 0); spi_send(MAX_DIGIT2, 0); spi_send(MAX_DIGIT3, 0); spi_send(MAX_DIGIT4, 0); spi_send(MAX_DIGIT5, 0); spi_send(MAX_DIGIT6, 0); spi_send(MAX_DIGIT7, 0); spi_send(MAX_SHUTDOWN, 1); // Wake oscillators/display up }这个例子是显示I love U, love是个爱心桃,哈哈,给女朋友瞅瞅


串口控制LED输出
接下来再加点东西,把之前用的串口拿过来用用,通过串口控制LED的显示,不过这里要提到一个取模,上面实际上也用到了,取模软件比较多,就不推荐了。

针对MAX7219 驱动,github 上有很多的开源项目,其中就有一些字库,我参考的是
这是现成的字库,不过输出的方向和我的相差个90度,我就自个写了个C程序把它给旋转了。生成后放到font.h , 字库太长就不放了,下面是结合串口通信写的驱动程序。
#include <msp430.h> #include <stdint.h> #include <stdio.h> #include <string.h> #include "font.h" void spi_send(uint8_t address, uint8_t data);void init_MAX7219(void);// MAX7219 Register addresses #define MAX_NOOP 0x00 #define MAX_DIGIT0 0x01 #define MAX_DIGIT1 0x02 #define MAX_DIGIT2 0x03 #define MAX_DIGIT3 0x04 #define MAX_DIGIT4 0x05 #define MAX_DIGIT5 0x06 #define MAX_DIGIT6 0x07 #define MAX_DIGIT7 0x08 #define MAX_DECODEMODE 0x09 #define MAX_INTENSITY 0x0A #define MAX_SCANLIMIT 0x0B #define MAX_SHUTDOWN 0x0C #define MAX_DISPLAYTEST 0x0F // 8x8 number font const uint8_t number[][8] = {{0x3c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c}, // 0 {0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c}, // 1 {0x3c, 0x66, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x7e}, // 2 {0x3c, 0x66, 0x06, 0x1e, 0x1e, 0x06, 0x66, 0x3c}, // 3 {0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00}, // heart-full {0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18}, // heart-full };const uint8_t iloveu[][8] = {{0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E}, // I {0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18}, // love, heart-full {0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18}, // U };const uint8_t create_heart[10][2] = {// row, value {0, 0x00},{2, 0x18},{1, 0x24},{1, 0x66},{2, 0x99},{3, 0x81},{4, 0x81},{5, 0x42},{6, 0x24},{7, 0x18},};void clear_screen() {int row;for (row = 0; row < 8; row++){spi_send(MAX_DIGIT0 + row, 0x00);}}// UART send char void send_char(char c){while (!(UCA1IFG&UCTXIFG)); // USCI_A1 TX buffer ready? UCA1TXBUF = c;}void send_strings(char *str){unsigned int i = 0;while(str != '