STM32硬件SPI控制TM1638按键数码管LED显示模块
从淘宝买来的,TM1638专门是控制LED的,LED组合起来就可以变成数码管,还有按键,这个我就没管了,不想管了,发这个帖子只是为了记录下经验,待以后会过头来看的时候,可以一遍就知道,所以尽量写得详细点。
手头上的项目用的是共阳极,而我买的是共阴极的,而且例程给的是STM32模拟IO实现的。
太浪费资源了,想尽办法折腾了1整体,终于搞定了SPI硬件实现的方式。写点东西给大家分享下,免得走弯路。本次重点介绍SPI硬件配置方面。
先来个照片,是共阴极的
原理图
废话不多说了,开始贴程序。
3.3V供电;
STM32F103c8t6;
SPI1;
SPI_Direction_1Line_Tx;
完整程序如下:
//TM1638.C文件
#ifdef USE_TM1638_SPI
#defineTM1638_SPIx SPI1
#defineTM1638_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
#defineTM1638_SPI_CLK RCC_APB2Periph_SPI1
#defineTM1638_SPI_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#defineTM1638_SPI_GPIO_CLK RCC_APB2Periph_GPIOA
#defineTM1638_SPI_SCK_PORT GPIOA
#defineTM1638_SPI_SCK_PIN GPIO_Pin_5
#defineTM1638_SPI_SCK_Low() GPIO_ResetBits(TM1638_SPI_SCK_PORT, TM1638_SPI_SCK_PIN)
#defineTM1638_SPI_SCK_High() GPIO_SetBits(TM1638_SPI_SCK_PORT, TM1638_SPI_SCK_PIN)
#defineTM1638_SPI_MISO_PORT GPIOA
#defineTM1638_SPI_MISO_PIN GPIO_Pin_6
#defineTM1638_SPI_MOSI_PORT GPIOA
#defineTM1638_SPI_MOSI_PIN GPIO_Pin_7
#defineTM1638_SPI_MOSI_Low() GPIO_ResetBits(TM1638_SPI_MOSI_PORT, TM1638_SPI_MOSI_PIN)
#defineTM1638_SPI_MOSI_High() GPIO_SetBits(TM1638_SPI_MOSI_PORT, TM1638_SPI_MOSI_PIN)
#defineTM1638_SPI_CS_CLK RCC_APB2Periph_GPIOA
#defineTM1638_SPI_CS_PORT GPIOA
#defineTM1638_SPI_CS_PIN GPIO_Pin_3
#defineTM1638_SPI_CS_ENABLE() GPIO_ResetBits(TM1638_SPI_CS_PORT, TM1638_SPI_CS_PIN)
#defineTM1638_SPI_CS_DISABLE() GPIO_SetBits(TM1638_SPI_CS_PORT, TM1638_SPI_CS_PIN)
#defineTM1638_CE_CLK RCC_APB2Periph_GPIOA
#defineTM1638_CE_PORT GPIOA
#defineTM1638_CE_PIN GPIO_Pin_3
#defineTM1638_CE_LOW() GPIO_ResetBits(TM1638_CE_PORT, TM1638_CE_PIN)
#defineTM1638_CE_HIGH() GPIO_SetBits(TM1638_CE_PORT, TM1638_CE_PIN)
#defineTM1638_IRQ_CLK RCC_APB2Periph_GPIOA
#defineTM1638_IRQ_PORT GPIOA
#defineTM1638_IRQ_PIN GPIO_Pin_2
#defineTM1638_IRQ_PIN_READ() GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)
voidTM1638_GPIO_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
TM1638_SPI_APBxClock_FUN(TM1638_SPI_CLK,ENABLE);
TM1638_SPI_GPIO_APBxClock_FUN(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5|GPIO_Pin_7|GPIO_Pin_6; //PB12上拉防止W25X的干扰
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化指定IO
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_4|GPIO_Pin_3; //PB12上拉防止W25X的干扰
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化指定IO
TM1638_SPI_CS_DISABLE(); //SPI片选取消
SPI_Cmd(TM1638_SPIx, DISABLE); // SPI外设不使能
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode =SPI_Mode_Master; //SPI主机
SPI_InitStructure.SPI_DataSize =SPI_DataSize_8b; //发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟悬空低
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //数据捕获于第1个时钟沿
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由软件控制
SPI_InitStructure.SPI_BaudRatePrescaler =SPI_BaudRatePrescaler_4; //定义波特率预分频的值:波特率预分频值为16
SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_LSB;//SPI_FirstBit_MSB; //数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(TM1638_SPIx,&SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(TM1638_SPIx, ENABLE); //使能SPI外设
//TM1638_CE_HIGH() ;
TM1638_SPI_CS_ENABLE(); //SPI片选取消
}
void TM1638_SPIx_SetSpeed(SPI_TypeDef*SPIx, uint8_t SPI_BaudRatePrescaler)
{
/* Check the parameters */
assert_param(IS_SPI_ALL_PERIPH(SPIx));
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPIx->CR1&=0XFFC7;
SPIx->CR1|=SPI_BaudRatePrescaler; //设置SPI2速度
SPI_Cmd(SPIx,ENABLE);
}
uint8_tTM1638_SPIx_ReadWriteByte(SPI_TypeDef* SPIx, uint8_t TxData)
{
SPI_I2S_SendData(SPIx, TxData); //通过外设SPIx发送一个数据
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY) == RESET);//等待发送完成
}
void TM1638_Write_Reg(uint8_t value)
{
TM1638_SPI_CS_ENABLE(); //使能SPI传输
TM1638_SPIx_ReadWriteByte(TM1638_SPIx,value); //写入寄存器的值
TM1638_SPI_CS_DISABLE(); //禁止SPI传输
}
voidWrite_DATA(unsigned char add,unsigned char DATA) //指定地址写入数据
{
TM1638_Write_Reg(0x44);
TM1638_SPI_CS_ENABLE() ;
//TM1638_Write(0xc0|add);
TM1638_SPIx_ReadWriteByte(TM1638_SPIx,0xc0|add); //设置起始地址
TM1638_SPIx_ReadWriteByte(TM1638_SPIx,DATA); //设置起始地址
//TM1638_Write(DATA);
TM1638_SPI_CS_DISABLE();
}
/*
voidWrite_oneLED(unsigned char num,unsigned char flag) //单独控制一个LED函数,num为需要控制的led序号,flag为0时熄灭,不为0时点亮
{
if(flag)
Write_DATA(2*num+1,1);
else
Write_DATA(2*num+1,0);
} */
voidWrite_allLED(unsigned char LED_flag) //控制全部LED函数,LED_flag表示各个LED状态
{
unsigned char i;
for(i=0;i<8;i++)
{
if(LED_flag&(1<<i))
//Write_DATA(2*i+1,3);
Write_DATA(2*i+1,1);
else
Write_DATA(2*i+1,0);
}
}
voidinit_TM1638(void)
{
unsigned char i;
TM1638_GPIO_init();
TM1638_SPIx_SetSpeed(TM1638_SPIx,SPI_BaudRatePrescaler_4);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)
TM1638_Write_Reg(0x8b); //亮度 (0x88-0x8f)8级亮度可调
TM1638_Write_Reg(0x40); //采用地址自动加1
TM1638_SPI_CS_ENABLE() ; //
TM1638_SPIx_ReadWriteByte(TM1638_SPIx,0xc0); //设置起始地址
for(i=0;i<16;i++) //传送16个字节的数据
TM1638_SPIx_ReadWriteByte(TM1638_SPIx,0x00);
TM1638_SPI_CS_DISABLE();
}
#endif
//main.C文件
/* 包含头文件 ----------------------------------------------------------------*/
#include"stm32f10x.h"
#include"bsp/usart/bsp_debug_usart.h"
#include"bsp/NRF24l01/NRF24l01.h"
#include"bsp/TM1638/bsp_tm1638.h"
unsignedchar LedTab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,
0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
staticvoid Delay(uint32_t time);
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
intmain(void)
{
uint8_ti;
uint8_t tmp_buf[33]="硬石开发板 NRF24L01 实验";
unsigned char num[8]; //各个数码管显示的值
/* 调试串口初始化配置,115200-N-8-1.使能串口发送和接受 */
DEBUG_USART_Init();
/* 调用格式化输出函数打印输出数据 */
printf("这是一个NRF24L012.4G无线数据传输模块测试实验\n");
init_TM1638();
/* 无限循环 */
Delay(200);
for(i=0;i<8;i++)
Write_DATA(i<<1,tab[0]); //初始化寄存器
while(1)
{
Delay(100);
if(i<8)
{
num[i]++;
//while(Read_key()==i); //等待按键释放
if(num[i]>15)
num[i]=0;
Write_DATA(i*2,tab[num[i]]);
Write_allLED(1<<i);
i++; //读按键值
}
else
i= 0;
}
}
/**
* 函数功能: 简单粗暴的延时函数
* 输入参数: time;延时时间设置
* 返 回 值: 无
* 说 明:使用这种死循环方式的延时函数是没办法精确控制延时时间长短,
* 只是大概的延时时间,所以基本上都是通过不断修改输入参数
* 大小然后下载到开发板上看实验效果。
*/
staticvoid Delay(uint32_t time)
{
uint32_t i,j;
for(i=0;i<time;++i)
{
for(j=0;j<10000;++j)
{
//空循环,什么都不做
}
}
}
如果实在搞不懂,发邮件给我[email protected]