一、说明
刚刚好在学习这个板子,总结了其他几篇文章,其中一些坑踩的我很难受,特别是一些时序的软件实现,想想都已经踩坑了,写下来一起交流学习吧。
参考文章:
STM32使用MAX7219控制点阵屏和点阵屏级联_stm32 max7219-CSDN博客
使用STM32的硬件SPI及软件模拟SPI的方式驱动MAX7219点阵_stm32 max7219-CSDN博客
51单片机LED8*8点阵显示坤坤跳舞打篮球画面_51单片机哥哥跳舞-CSDN博客
二、硬件结构
1、引脚结构
- V+:正电源
- GND:地
- CLK:移位脉冲输入端
- DIN:串行数据输入端
- CS:片选
我们使用的是共阴极的点阵屏,因为有芯片控制,我们只要配置对应的寄存器即可
下面是一些引脚说明,这个支持SPI通信,不过没有主机接收的线路,只有主机发送,如下图MOSI
2、内部结构
看这个图,移位寄存器是16位的,所以我写代码的时候,也是写够16位才把cs切换为高电平(低电平有效)。
- 高位无所谓,给0吧。反正无效。然后发送的数据帧大概就是无效位+地址位+数据位
- 其实每一行对应一个寄存器存储数据,存着8位数据,寄存器具体大小手册没有说,不用在意,知道8位就好。因为数据帧里面也规定了,数据位8位。
- 所以,我都是每次发送8位,到第16位之后,CS切换为高电平,这个点比较重要。不单是使能和失能,还会存储对应数据。
3、寄存器分布
这个的理解就是,8个REG_x,对应8行,然后每个寄存器存储8位数据,根据我的实验,比如第一行是1111 1111 那么第一行就全亮,0不亮,1亮的意思。
然后剩下的一些就是配置寄存器的内容了。
4、时序图
看蓝色部分就行,CS低电平的时候才工作,时钟上升沿发数据,下降沿变化数据。
5、板子引脚的配置
都配置普通推挽模式就行,其实CS也可以配置上拉,我是为了简单。
三、代码
LatticeLed_Ins.h
#ifndef __LARRICELED_INS_H
#define __LARRICELED_INS_H
#define CLK_PIN GPIO_Pin_3
#define CS_PIN GPIO_Pin_4
#define DIN_PIN GPIO_Pin_5
#define DECODEMODE 0X09 //编码模式
#define INTENSITY 0X0a //亮度
#define SCANLIMT 0x0b //扫描寄存器个数
#define SHUTDOWN 0X0c //关闭寄存器
#define DISPLAYTEST 0X0f //显示测试
#define CLK(x) GPIO_WriteBit(GPIOA , CLK_PIN, (BitAction)(x));
#define CS(x) GPIO_WriteBit(GPIOA , CS_PIN, (BitAction)(x));
#define DIN(x) GPIO_WriteBit(GPIOA , DIN_PIN, (BitAction)(x));
#endif
LatticeLed.h
#ifndef __LatticeLed_H
#define __LatticeLed_H
void LatticeLedInit(void);
void LatticeLedWrite_byte(uint8_t data);
void LatticeLed_Write_Command(uint8_t addr,uint8_t data);
void StartWrite(void);
void StopWrite(void);
void LatticeLedCLS(void);
#endif
LatticeLed.c
#include "stm32f10x.h" // Device header
#include "LatticeLed_Ins.h"
void StartWrite(void)
{
CS(0);
}
void StopWrite(void)
{
CS(1);
}
void LatticeLedWrite_byte(uint8_t data)//单字节写入
{
uint8_t i=0;
for(i=8;i>0;i--)
{
if(data&0x80)//高位先行
{
DIN(1);
}
else
{
DIN(0);
}
CLK(1);
CLK(0);
data=data<<1;//最高位左移以为 次高位变最高位
}
}
void LatticeLed_Write_Command(uint8_t addr,uint8_t data)//写命令
{
CLK(0);
StartWrite(); //确保CS处于低电平
LatticeLedWrite_byte(addr);//寄存器地址
LatticeLedWrite_byte(data);//需要写入的数据
StopWrite();
}
void LatticeLedInit(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = CLK_PIN | CS_PIN| DIN_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化电平确保一下时序
StopWrite();
CLK(0);
LatticeLed_Write_Command(DECODEMODE,0X00); //译码方式:无
LatticeLed_Write_Command(INTENSITY,0X01); //亮度
LatticeLed_Write_Command(SCANLIMT,0X07); //扫描界限;8个数码管显示
LatticeLed_Write_Command(SHUTDOWN,0X01); //掉电模式:0,普通模式:1
LatticeLed_Write_Command(DISPLAYTEST,0X00); //显示测试:1;测试结束,正常显示:0
}
void LatticeLedCLS(void)
{
uint8_t i;
for(i=8;i>=1;i--)
{
LatticeLed_Write_Command(i,0x00);
}
}
Delay.h
#ifndef __DELAY_H
#define __DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
Delay.c
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
坤坤爱篮球
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LatticeLed.h"
const uint8_t myarray[4][8]={//38行,8列
{0x00,0x00,0x1B,0x3C,0xF8,0xFC,0x3B,0x00},{0x00,0x1B,0x3C,0xFC,0xFF,0x3A,0x00,0x00},
{0x00,0x00,0x0B,0x1C,0x7C,0x7E,0x1B,0x00},{0x00,0x1B,0x3C,0xFC,0xFF,0x3A,0x00,0x00}
};
void show_number(uint8_t j)
{
uint8_t i;
for(i=1;i<9;i++)//1到8行
{
LatticeLed_Write_Command(i,myarray[j][i-1]); //行1-8地址 列数据
}
}
int main(void) {
LatticeLedInit();
LatticeLedCLS();
uint8_t i;
while (1)
{
for(i=0;i<4;i++)
{
show_number(i);
Delay_ms(100);
}
}
}