2869|2

13

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

【MSP430F5529测评】4. SPI 通信 之 MAX7219 驱动LED点阵 [复制链接]

本帖最后由 wuguangtao 于 2020-11-10 22:14 编辑

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段译码管。

 

这个点阵屏是可以级联的,通过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 上有很多的开源项目,其中就有一些字库,我参考的是

  • https://github.com/riyas-org/max7219/blob/master/max7219-rpi/src/font.py

这是现成的字库,不过输出的方向和我的相差个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 != '\0') {
        send_char(str);
        i++;
    }
}

void flash_led()
{
    uint8_t row, index;

    for (index = 3; index > 0; index--)
    {
        // Load all 8 row/digit registers with data from number[index]
        for (row = 0; row < 8; row++)
        {
            spi_send(MAX_DIGIT0 + row, number[index][row]);
        }
        __delay_cycles(500000); // Wait a bit before showing next frame
        clear_screen();
    }
    __delay_cycles(500000); // Wait a bit before showing next frame

    for (index = 0; index < 3; index++)
    {
        // Load all 8 row/digit registers with data from number[index]
        for (row = 0; row < 8; row++)
        {
            spi_send(MAX_DIGIT0 + row, iloveu[index][row]);
        }
        __delay_cycles(500000); // Wait a bit before showing next frame
        clear_screen();
    }
    __delay_cycles(500000); // Wait a bit before showing next frame

    for (index = 0; index < 10; index++)
    {
        spi_send(MAX_DIGIT0 + create_heart[index][0], create_heart[index][1]);
        __delay_cycles(200000); // Wait a bit before showing next frame
    }

    __delay_cycles(500000); // Wait a bit before showing next frame
    int i = 0;
    while (i++ < 3)
    {
        // Loop through some frames
        for (index = 4; index < 6; index++)
        {
            // Load all 8 row/digit registers with data from number[index]
            if (index == 4)
                spi_send(MAX_INTENSITY, 0x07);
            else
                spi_send(MAX_INTENSITY, 0x02);
            for (row = 0; row < 8; row++)
            {
                spi_send(MAX_DIGIT0 + row, number[index][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
}


void show_char(uint8_t ch)
{
    uint8_t row;

    clear_screen();
    // Load all 8 row/digit registers with data from ASCII_FONTS[ch]
    for (row = 0; row < 8; row++)
    {
        spi_send(MAX_DIGIT0 + row, ASCII_FONTS[ch][row]);
    }
}


uint8_t chr = 0;
int main(void)
{

    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

    // USCI_A1 UART
    P4SEL |= BIT4+BIT5;                       // P4.4,5 = USCI_A1 TXD/RXD
    UCA1CTL1 |= UCSWRST;                      // **Put state machine in reset**
    UCA1CTL1 |= UCSSEL_2;                     // SMCLK
    UCA1BR0 = 9;                              // 1MHz 115200 (see User's Guide)
    UCA1BR1 = 0;                              // 1MHz 115200
    UCA1MCTL |= UCBRS_1 + UCBRF_0;            // Modulation UCBRSx=1, UCBRFx=0
    UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
    UCA1IE |= UCRXIE;                         // Enable USCI_A1 RX interrupt

    // MAX7219 SPI
    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);

    __bis_SR_register( GIE);                   // interrupts enabled

    init_MAX7219();
    __delay_cycles(1000);


    flash_led();
    uint8_t old_chr = 0;
    while(1) {
//        __bis_SR_register(LPM0_bits);
        if (old_chr != chr) {
            show_char(chr);
            old_chr = chr;
        }
        __delay_cycles(1000);
    }
}

// Echo back RXed character, confirm TX buffer is ready first
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A1_VECTOR
__interrupt void USCI_A1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_A1_VECTOR))) USCI_A1_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch(__even_in_range(UCA1IV,4))
  {
  case 0:break;                             // Vector 0 - no interrupt
  case 2:                                   // Vector 2 - RXIFG
    while (!(UCA1IFG&UCTXIFG));             // USCI_A1 TX buffer ready?
    chr = UCA1RXBUF;
    UCA1TXBUF = chr;                        // TX -> RXed character
//    __bic_SR_register_on_exit(LPM0_bits);
    break;
  case 4:break;                             // Vector 4 - TXIFG
  default: break;
  }
}

 

在UART中断中把接收到的字符存在buf中,主循环去判断是否有更新,如果有就驱动LED更新显示。

比如,咱显示个大写字母Q

哈哈,是不是很有趣,其实可以搞些好玩的动画,横屏或者竖屏滚动,或者旋转,然后配合时间控制以及亮度调节,会更好玩。时间有限就先这样吧。

参考资料

  • https://github.com/evilbetty/msp430_max7219_ledmatrix
  • https://github.com/riyas-org/max7219/blob/master/max7219-rpi/src/font.py

MAX7219-MAX7221_cn.pdf (1.35 MB, 下载次数: 8)

最新回复

不错的分享。     详情 回复 发表于 2020-11-15 20:52
 
点赞 关注

回复
举报

7671

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

居然有女朋友,不可思议的事情。。。

 
个人签名

默认摸鱼,再摸鱼。2022、9、28

 

回复

2618

帖子

0

TA的资源

纯净的硅(高级)

板凳
 

不错的分享。

 

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表