只是想简单地用8*8LED点阵实现一下类电子沙漏的东西。没有陀螺仪,没有改变方向自动找平的功能,只实现最简单的下落堆积的功能。对,就是个伪电子沙漏,那么开始吧...
主控:CH32V307
8*8点阵驱动:2个595
这个模块级联没有引出
一、源码
/********************************** (C) COPYRIGHT *******************************
* File Name : main.c
* Author : WCH
* Version : V1.0.0
* Date : 2021/06/06
* Description : Main program body.
*********************************************************************************
* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
* Attention: This software (modified or not) and binary are used for
* microcontroller manufactured by Nanjing Qinheng Microelectronics.
*******************************************************************************/
#include "debug.h"
/*在中断里面调用的变量需要使用 volatile 修饰*/
volatile uint8_t gpio_interrupt_flag=0;
/* Global typedef */
/* 595 这里只定义了1个595的3个pin,应该有第2个595,实现从上面的沙漏到下一沙漏,我暂时只用了1个595,每次只显示上面或下面一个沙漏*/
#define LED_SER GPIO_Pin_13
#define LED_RCK GPIO_Pin_14
#define LED_SRCK GPIO_Pin_15
#define LED_SER_SET GPIO_WriteBit(GPIOB, LED_SER,Bit_SET)
#define LED_SER_CLR GPIO_WriteBit(GPIOB, LED_SER,Bit_RESET)
#define LED_SRCK_SET GPIO_WriteBit(GPIOB, LED_SRCK,Bit_SET)
#define LED_SRCK_CLR GPIO_WriteBit(GPIOB, LED_SRCK,Bit_RESET)
#define LED_RCK_SET GPIO_WriteBit(GPIOB, LED_RCK,Bit_SET)
#define LED_RCK_CLR GPIO_WriteBit(GPIOB, LED_RCK,Bit_RESET)
/* Global define */
//用两个数组分布代表上下2个沙漏的数据,低电平点亮
//可以理解为显存,每个显存8个字节,8*8=64bit,对应8*8LED的64个点
u8 matrix1[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
u8 matrix2[8]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
//由于LED的COM和SEG都是595控制,需要定义COM的控制数据,用于点亮一行,高电平有效
//0x01代表第一个行,以此类推
const u8 pos[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
/* Global Variable */
/*
* @prief GPIO初始化
*/
void LED_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
EXTI_InitTypeDef EXTI_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = LED_SER|LED_RCK|LED_SRCK;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO |RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*设置PA2作为中断线的GPIO引脚*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2);
/*设置GPIO中断*/
EXTI_InitStructure.EXTI_Line = EXTI_Line0;//中断线0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能
EXTI_Init(&EXTI_InitStructure);
/*配置中断优先等级*/
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//外部中断0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占式优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应式优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 中断函数
* 按键中断和沙漏无关
**/
__attribute__((interrupt("WCH-Interrupt-fast"))) //中断函数前加这上这句,告诉编译器这个是中断函数
void EXTI0_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0)!=RESET)//产生中断
{
gpio_interrupt_flag=1;
EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志
}
}
/*
* @prief 控制595串行输入
* PN:反显
* */
void HC595(u8 c,u8 PN)
{
u8 i;
for(i=0;i<8;i++){
if(PN==0){ //正常显示
if((c>>i) & 0x01) LED_SER_SET;
else LED_SER_CLR;
}
else{ //反显
if((c>>i) & 0x01) LED_SER_CLR;
else LED_SER_SET;
}
LED_SRCK_CLR;
LED_SRCK_SET; // 上升沿进行一次数据移入
}
}
/*
* @prief 显示pBuff指向的8个字节数据的内容到8*8led
*
*/
void Matrix8x8(u8 *pBuff)
{
unsigned char k;
unsigned int m,n;
for(m=0;m<32;m++) //为移动预留
{
for(n=0;n<32;n++)//控制显示速度,防止闪烁
{
for(k=0;k<8;k++) //行扫描
{
HC595(~pos[k],0);
HC595(~pBuff[k],0);
Delay_Us(10);
LED_RCK_CLR;
LED_RCK_SET; //并行输出
}
}
}
}
/*
* @prief 上沙漏最下一个点不断闪烁
*/
void sandflash(u8 bright)
{
u8 oldVal=matrix1[7];
if(bright==1){
matrix1[7]=(0x7F&oldVal)|0x80;
Matrix8x8(matrix1);
}else{
matrix1[7]=0x7F&oldVal;
Matrix8x8(matrix1);
}
matrix1[7]=oldVal; //
}
/*
* @prief 初始化沙漏
* @param matrix:8字节显存;val:初始的内容
*/
void resetHourglass(u8 *matrix,u8 val)
{
u8 i;
for(i=0;i<8;i++){
matrix[i]=val;
}
}
/*
* @prief 上沙漏演示
*/
void hourglass1()
{
u8 i,j;
for(j=0;j<15;j++){ //上沙漏,需要循环14次,将沙子漏完
if(j<8){ //上8个和后面处理不同
for(i=0;i<=j/2;i++){ //分成2部分
sandflash(1);
matrix1[i] =(matrix1[i] << 1) | 0x01; //左半部分处理,就是左移,或上第1位
Matrix8x8(matrix1);
sandflash(0);
if(i!=j%8-i){ //如果前面没处理,那么执行
sandflash(1);
matrix1[j%8-i] =(matrix1[j%8-i] << 1) | 0x01; //右半部分,也是就是左移,或上第1位
Matrix8x8(matrix1);
sandflash(0);
}
}
}
else{
for(i=0;i<((7-j%8)-1)/2+1;i++){ //上沙漏,下半部分处理,也是分左右两半
sandflash(1);
matrix1[i+j%8+1] =(matrix1[i+j%8+1] << 1) | 0x01; //左移,或上第1位
Matrix8x8(matrix1);
sandflash(0);
if(i+j%8+1!=j-(i+j%8+1)){
sandflash(1);
matrix1[j-(i+j%8+1)] =(matrix1[j-(i+j%8+1)] << 1) | 0x01; //左移,或上第1位
Matrix8x8(matrix1);
sandflash(0);
}
}
}
}
}
/*
* @prief 下沙漏一个沙子向下落
*/
void sandflow(u8 step)
{
u8 i;
for(i=0;i<step;i++){ //step是下落步数
matrix2[i]=matrix2[i]&~(0x01<<i); //显示
Matrix8x8(matrix2);
matrix2[i]=matrix2[i]|(0x01<<i); //消隐
Matrix8x8(matrix2);
}
}
/*
* @prief 下沙漏演示
* 点亮低电平有效
*/
void hourglass2(void)
{
u8 i,j;
for(i=0;i<8;i++){
for(j=0;j<i;j++)
{
sandflow(8-i);
matrix2[7-j]=matrix2[7-j]&~(0x80>>i); //每一行都要设置一个(0x80>>i)
Matrix8x8(matrix2);
sandflow(8-i);
matrix2[7-i]=matrix2[7-i]&~(0x80>>j); //第7-i行要设置(0x80>>j)
Matrix8x8(matrix2);
}
sandflow(8-i);
matrix2[7-i]=matrix2[7-i]&~(0x80>>i); //第7-i行也要设置一个(0x80>>i)
Matrix8x8(matrix2);
}
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
printf("This is printf example\r\n");
LED_GPIO_Init();
while(1)
{
/*按键中断,预留,和沙漏演示无关*/
if (gpio_interrupt_flag==1) {//有中断产生
gpio_interrupt_flag=0;
}
//上沙漏演示
hourglass1();
//初始化上沙漏显存
resetHourglass(matrix1,0x00);
//下沙漏演示
hourglass2();
//初始化下沙漏显存
resetHourglass(matrix2,0xFF);
}
}
二、演示
沙漏演示2
沙漏演示