找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 194|回复: 3
打印 上一主题 下一主题
收起左侧

一个STC单片机驱动MAX7219的音乐节拍器程序

[复制链接]
跳转到指定楼层
楼主
ID:39720 发表于 2026-5-3 08:10 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
main.c
  1. #include <reg52.h>
  2. #include <intrins.h>
  3. #include "keyscan.h"

  4. // 定时器0初始化
  5. void Timer0_Init()
  6. {
  7.     TMOD |= 0x01;      // 定时器0,工作模式1
  8.     TH0 = 0xFC;        // 1ms定时
  9.     TL0 = 0x18;
  10.     ET0 = 1;           // 允许定时器0中断
  11.     TR0 = 1;           // 启动定时器0
  12.     EA = 1;            // 开启总中断
  13. }

  14. // 主函数
  15. void main()
  16. {
  17.     // 初始化
  18.     P0 = 0xFF;
  19.     P1 = 0xFF;
  20.     P2 = 0x00;  // LED初始全灭
  21.     P3 = 0xFF;

  22.     MAX7219_Init();
  23.     Clear_Display();
  24.     Display_Speed();
  25.     Display_BeatType();

  26.     Timer0_Init();

  27.     while(1)
  28.     {
  29.         Key_Scan();
  30.     }
  31. }

  32. // 定时器0中断服务程序
  33. void Timer0_ISR() interrupt 1
  34. {
  35.     TH0 = 0xFC;        // 重新装载初值
  36.     TL0 = 0x18;

  37.     timer_count++;

  38.     if(is_playing)
  39.     {
  40.         if(timer_count >= interval)
  41.         {
  42.             timer_count = 0;

  43.             // 产生节拍
  44.             Update_LEDs();
  45.             Output_Sound();

  46.             // 更新拍数
  47.             beat_count++;
  48.             if(beat_count >= beats_per_bar[beat_type])
  49.             {
  50.                 beat_count = 0;
  51.             }
  52.         }
  53.     }
  54. }



  55. keyscan.c
  56. #include "keyscan.h"
  57. unsigned int interval = 0;
  58. unsigned char is_playing = 0;
  59. unsigned char beat_count = 0;
  60. unsigned int timer_count = 0;
  61. // 按键扫描
  62. void Key_Scan()
  63. {
  64.     static unsigned char key_up_flag = 1;
  65.     static unsigned char key_down_flag = 1;
  66.     static unsigned char key_type_flag = 1;
  67.     static unsigned char key_start_flag = 1;

  68.     // 速度+按键
  69.     if(KEY_UP == 0)
  70.     {
  71.         if(key_up_flag)
  72.         {
  73.             key_up_flag = 0;
  74.             if(beat_speed < 240)
  75.             {
  76.                 beat_speed += 1;
  77.                 Display_Speed();
  78.                 if(is_playing)
  79.                 {
  80.                     // 重新计算间隔
  81.                     interval = 60000 / beat_speed;  // ms
  82.                 }
  83.             }
  84.         }
  85.     }
  86.     else
  87.     {
  88.         key_up_flag = 1;
  89.     }

  90.     // 速度-按键
  91.     if(KEY_DOWN == 0)
  92.     {
  93.         if(key_down_flag)
  94.         {
  95.             key_down_flag = 0;
  96.             if(beat_speed > 40)
  97.             {
  98.                 beat_speed -= 1;
  99.                 Display_Speed();
  100.                 if(is_playing)
  101.                 {
  102.                     interval = 60000 / beat_speed;  // ms
  103.                 }
  104.             }
  105.         }
  106.     }
  107.     else
  108.     {
  109.         key_down_flag = 1;
  110.     }

  111.     // 拍型选择按键
  112.     if(KEY_TYPE == 0)
  113.     {
  114.         if(key_type_flag)
  115.         {
  116.             key_type_flag = 0;
  117.             beat_type = (beat_type + 1) % 4;
  118.             beat_count = 0;
  119.             Display_BeatType();
  120.             Update_LEDs();
  121.         }
  122.     }
  123.     else
  124.     {
  125.         key_type_flag = 1;
  126.     }

  127.     // 开始/停止按键
  128.     if(KEY_START == 0)
  129.     {
  130.         if(key_start_flag)
  131.         {
  132.             key_start_flag = 0;
  133.             is_playing = !is_playing;
  134.             if(is_playing)
  135.             {
  136.                 beat_count = 0;
  137.                 interval = 60000 / beat_speed;  // 计算每个节拍的时间间隔(ms)
  138.                 timer_count = 0;
  139.                 // 显示运行状态
  140.                 MAX7219_WriteCmd(5, 0x80);  // 在第五位显示点
  141.             }
  142.             else
  143.             {               
  144.                 MAX7219_WriteCmd(5, 0x00);      // 显示停止状态               
  145.                 LED1 = LED2 = LED3 = LED4 = LED5 = LED6 = 0;  // 关闭所有LED
  146.             }
  147.         }
  148.     }
  149.     else
  150.     {
  151.         key_start_flag = 1;
  152.     }
  153. }

  154. void delay_ms(unsigned int ms)
  155. {
  156.     unsigned int i, j;
  157.     for(i = 0; i < ms; i++)
  158.         for(j = 0; j < 114; j++);
  159. }
  160. // 更新指示灯
  161. void Update_LEDs()
  162. {
  163.     LED1 = LED2 = LED3 = LED4 = LED5 = LED6 = 0;  // 先关闭所有LED

  164.     switch(beat_type)
  165.     {
  166.         case 0:  // 2/4拍
  167.             if(beat_count == 0) LED1 = 1;  // 强拍
  168.             if(beat_count == 1) LED2 = 1;  // 弱拍
  169.             break;

  170.         case 1:  // 3/4拍
  171.             if(beat_count == 0) LED1 = 1;  // 强拍
  172.             if(beat_count == 1) LED2 = 1;  // 弱拍
  173.             if(beat_count == 2) LED3 = 1;  // 弱拍
  174.             break;

  175.         case 2:  // 4/4拍
  176.             if(beat_count == 0) LED1 = 1;  // 强拍
  177.             if(beat_count == 1) LED2 = 1;  // 弱拍
  178.             if(beat_count == 2) LED3 = 1;  // 次强拍
  179.             if(beat_count == 3) LED4 = 1;  // 弱拍
  180.             break;

  181.         case 3:  // 6/8拍
  182.             if(beat_count == 0) LED1 = 1;  // 强拍
  183.             if(beat_count == 1) LED2 = 1;  // 弱拍
  184.             if(beat_count == 2) LED3 = 1;  // 弱拍
  185.             if(beat_count == 3) LED4 = 1;  // 次强拍
  186.             if(beat_count == 4) LED5 = 1;  // 弱拍
  187.             if(beat_count == 5) LED6 = 1;  // 弱拍
  188.             break;
  189.     }
  190. }

  191. // 输出音源信号
  192. void Output_Sound()
  193. {
  194.     // 根据拍型输出不同的音源
  195.     switch(beat_type)
  196.     {
  197.         case 0:  // 2/4拍
  198.             if(beat_count == 0)
  199.             {
  200.                 SOUND1 = 1;  // 强拍
  201.                 delay_ms(20);
  202.                 SOUND1 = 0;
  203.             }
  204.             else
  205.             {
  206.                 SOUND2 = 1;  // 弱拍
  207.                 delay_ms(10);
  208.                 SOUND2 = 0;
  209.             }
  210.             break;

  211.         case 1:  // 3/4拍
  212.             if(beat_count == 0)
  213.             {
  214.                 SOUND1 = 1;  // 强拍
  215.                 delay_ms(20);
  216.                 SOUND1 = 0;
  217.             }
  218.             else
  219.             {
  220.                 SOUND3 = 1;  // 弱拍
  221.                 delay_ms(10);
  222.                 SOUND3 = 0;
  223.             }
  224.             break;

  225.         case 2:  // 4/4拍
  226.             if(beat_count == 0)
  227.             {
  228.                 SOUND1 = 1;  // 强拍
  229.                 delay_ms(20);
  230.                 SOUND1 = 0;
  231.             } else if(beat_count == 2)
  232.             {
  233.                 SOUND2 = 1;  // 次强拍
  234.                 delay_ms(15);
  235.                 SOUND2 = 0;
  236.             }
  237.             else
  238.             {
  239.                 SOUND3 = 1;  // 弱拍
  240.                 delay_ms(10);
  241.                 SOUND3 = 0;
  242.             }
  243.             break;

  244.         case 3:  // 6/8拍
  245.             if(beat_count == 0)
  246.             {
  247.                 SOUND1 = 1;  // 强拍
  248.                 delay_ms(20);
  249.                 SOUND1 = 0;
  250.             }
  251.             else if(beat_count == 3)
  252.             {
  253.                 SOUND2 = 1;  // 次强拍
  254.                 delay_ms(15);
  255.                 SOUND2 = 0;
  256.             }
  257.             else
  258.             {
  259.                 SOUND4 = 1;  // 弱拍
  260.                 delay_ms(10);
  261.                 SOUND4 = 0;
  262.             }
  263.             break;
  264.     }
  265. }


  266. diplay.c
  267. #include "display.h"

  268. unsigned int beat_speed = 120;      // 节拍速度(BPM),默认120
  269. unsigned char beat_type = 0;        // 拍型索引 0:2/4, 1:3/4, 2:4/4, 3:6/8
  270. // 数字字体(0-9)

  271. unsigned char code font_table[] =
  272. {
  273.     0x7E, 0x30, 0x6D, 0x79, 0x33,
  274.     0x5B, 0x5F, 0x70, 0x7F, 0x7B
  275. };

  276. // 拍型对应的每小节拍数
  277. unsigned char code beats_per_bar[] = {2, 3, 4, 6};

  278. // MAX7219初始化命令
  279. unsigned char code init_cmds[][2] =
  280. {
  281.     {0x0C, 0x01},  // 关闭关机模式
  282.     {0x0B, 0x07},  // 扫描所有8位
  283.     {0x0A, 0x08},  // 亮度
  284.     {0x09, 0x00},  // 解码模式:无
  285.     {0x0F, 0x00}   // 显示测试:关闭
  286. };



  287. // MAX7219写一个字节
  288. void MAX7219_WriteByte(unsigned char data_byte)
  289. {
  290.     unsigned char i;
  291.     for(i = 0; i < 8; i++)
  292.     {
  293.         MAX7219_CLK = 0;
  294.         MAX7219_DIN = (data_byte & 0x80) ? 1 : 0;
  295.         data_byte <<= 1;
  296.         MAX7219_CLK = 1;
  297.     }
  298. }

  299. // MAX7219写命令
  300. void MAX7219_WriteCmd(unsigned char address, unsigned char data_byte)
  301. {
  302.     MAX7219_CS = 0;
  303.     MAX7219_WriteByte(address);
  304.     MAX7219_WriteByte(data_byte);
  305.     MAX7219_CS = 1;
  306. }

  307. // MAX7219初始化
  308. void MAX7219_Init()
  309. {
  310.     unsigned char i;
  311.     for(i = 0; i < 5; i++)
  312.     {
  313.         MAX7219_WriteCmd(init_cmds[i][0], init_cmds[i][1]);
  314.     }
  315. }

  316. // 在数码管上显示数字
  317. void Display_Number(unsigned char digit, unsigned char number)
  318. {
  319.     if(digit >= 1 && digit <= 8)
  320.     {
  321.         MAX7219_WriteCmd(digit, number);
  322.     }
  323. }

  324. // 显示当前速度
  325. void Display_Speed()
  326. {
  327.     unsigned char bai, shi, ge;

  328.     bai = beat_speed / 100;
  329.     shi = (beat_speed % 100) / 10;
  330.     ge = beat_speed % 10;

  331.     // 显示在4-6位数码管上
  332.     Display_Number(3, font_table[bai]);
  333.     Display_Number(2, font_table[shi]);
  334.     Display_Number(1, font_table[ge]);
  335. }

  336. // 显示拍型
  337. void Display_BeatType()
  338. {
  339.     switch(beat_type)
  340.     {
  341.         case 0: Display_Number(8, 0x6D);         //2
  342.                 Display_Number(7, 0x01);
  343.                 Display_Number(6, 0x33); break;  // 4
  344.         case 1: Display_Number(8, 0x79);         // 3
  345.                 Display_Number(7, 0x01);
  346.                 Display_Number(6, 0x33); break;  // 4
  347.         case 2: Display_Number(8, 0x33);         // 4
  348.                 Display_Number(7, 0x01);
  349.                 Display_Number(6, 0x33); break;  // 4
  350.         case 3: Display_Number(8, 0x5F);         // 6
  351.                 Display_Number(7, 0x01);
  352.                 Display_Number(6, 0x7F); break;  // 8
  353.     }
  354. }

  355. // 清除所有显示
  356. void Clear_Display()
  357. {
  358.     unsigned char i;
  359.     for(i = 1; i <= 8; i++)
  360.     {
  361.         MAX7219_WriteCmd(i, 0x00);
  362.     }
  363. }

复制代码


评分

参与人数 1黑币 +10 收起 理由
wpppmlah + 10 赞一个!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:39720 发表于 2026-5-3 08:14 | 只看该作者
我的压缩文件怎么发呀?
回复

使用道具 举报

板凳
ID:1064915 发表于 2026-5-4 08:10 | 只看该作者
MAX7219 是美国MAXIM 公司推出的多位LED 显示驱动器,采用3 线串行接口传送数据,可直接与单片机接口连接,用户能方便修改其内部参数,以实现多位LED 显示。它内含硬件动态扫描电路、BCD译码器、段驱动器和位驱动器。
回复

使用道具 举报

地板
ID:477512 发表于 2026-5-5 08:01 | 只看该作者
压缩文件在哪里?
回复

使用道具 举报

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

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表