本帖最后由 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)