#include <LiquidCrystal_I2C.h>
#include <stdio.h>
#include <time.h>
#include <Ticker.h>
/// 按钮从外部中断数码管
// 与主程序共享的变量要加上 volatile 关键字
#define BUTTON_PIN 18
volatile bool flag = false;
void setTubeExternalInterrupt();
void toggleTubeExternalInterrupt();
// 外部中断函数
void handle_interrupt()
{
flag = true;
}
// 定义数码管的输出引脚并把所有引脚存到数组中
int pin_a = 39;
int pin_b = 40;
int pin_c = 42;
int pin_d = 2;
int pin_e = 1;
int pin_f = 38;
int pin_g = 37;
int pin_dp = 41;
int pin_array[8] = {pin_a, pin_b, pin_c, pin_d, pin_e, pin_f, pin_g, pin_dp};
// 定义数字显示逻辑的二维数组
int number_array[][8] = {
{0, 0, 0, 0, 0, 0, 1, 1}, // 0
{1, 0, 0, 1, 1, 1, 1, 1}, // 1
{0, 0, 1, 0, 0, 1, 0, 1}, // 2
{0, 0, 0, 0, 1, 1, 0, 1}, // 3
{1, 0, 0, 1, 1, 0, 0, 1}, // 4
{0, 1, 0, 0, 1, 0, 0, 1}, // 5
{0, 1, 0, 0, 0, 0, 0, 1}, // 6
{0, 0, 0, 1, 1, 1, 1, 1}, // 7
{0, 0, 0, 0, 0, 0, 0, 1}, // 8
{0, 0, 0, 0, 1, 0, 0, 1}, // 9
};
/// 硬件定时器中断数码管显示数字
hw_timer_t *timer_tube = NULL;
volatile int cur_num=0;
void setTubeHardwareTimerInterrupt();
// 硬件定时器中断处理函数
void timer_tube_interrupt()
{
/// 显示数字
// 清屏
for (int j=0; j<8; j++)
{
digitalWrite(pin_array[j], HIGH);
}
// 改变对应引脚的电平;
for (int j=0; j<8; j++)
{
digitalWrite(pin_array[j], number_array[cur_num][j]);
}
if ((++cur_num)>9) cur_num=0;
}
/// 硬件定时器中断控制LED灯
// 定义 led 灯的GPIO 引脚数组
int pin_list[4] = {0, 48, 21, 19};
// 获取数组长度
int size = sizeof(pin_list) / sizeof(pin_list[0]);
hw_timer_t *timer_led = NULL;
volatile int cur_led=0, last_led;
void setLedHardwareTimerInterrupt();
// 中断处理函数
void timer_led_interrupt()
{
if(cur_led>3) cur_led=0;
for(int i=0; i<size; i++)
{
digitalWrite(pin_list[i], LOW);
}
digitalWrite(pin_list[cur_led++], HIGH);
}
/*
* 软件定时器中断控制LCD屏幕
*/
// 使用 PCF8574T 液晶屏驱动模块的 I²C 地址建立一个 16 列 2 行的液晶对象
// 引脚必须对应 ESP32 的 I2C 引脚的 SDA 对应 8,SCL 对应 9
LiquidCrystal_I2C lcd (0x27, 16, 2);
int LCD_SCL_PIN=8;
// 定义定时器对象
Ticker timer_lcd;
/*
* Main
*/
void setup()
{
// 开启串口通信
Serial.begin(115200);
initTubeHardwareTimerInterrupt();
setTubeExternalInterrupt();
setLedHardwareTimerInterrupt();
setLcd();
}
void loop()
{
toggleTubeExternalInterrupt();
execLcdSerial();
}
/// 按钮从外部中断数码管
void setTubeExternalInterrupt()
{
pinMode(BUTTON_PIN, INPUT_PULLDOWN);
// for (int i=0;i<8;i++)
// {
// pinMode(pin_array[i], OUTPUT);
// }
// 设置所有引脚为输出模式,初始化所有引脚为高电平
/* 共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,
共阳数码管在应用时应将公共极 COM 接到 +5V ,
当某一字段发光二极管的阴极为低电平时,相应字段就点亮,
当某一字段的阴极为高电平时,相应字段就不亮。
*/
for (int i=0;i<8;i++)
{
pinMode(pin_array[i], OUTPUT);
digitalWrite(pin_array[i], LOW);
}
/* 外部中断配置 attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) 包括 3 个参数:
pin:GPIO 端口号;
ISR:中断服务程序,没有参数与返回值的函数;
mode:中断触发的方式,支持以下触发方式:
LOW 低电平触发
HIGH 高电平触发
RISING 上升沿触发
FALLING 下降沿触发
CHANGE 电平变化触发
*/
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handle_interrupt, FALLING);
}
void toggleTubeExternalInterrupt()
{
if (flag)
{
cur_num=0;
Serial.println((String)"外部中断触发, flag="+flag);
setTubeHardwareTimerInterrupt(1);
delay(10000);
setTubeHardwareTimerInterrupt(0);
// 重置中断标志位
flag = false;
}
}
/// 硬件定时器中断数码管显示数字(无loop)
void initTubeHardwareTimerInterrupt()
{
// 初始化定时器
timer_tube = timerBegin(0, 80, true);
timerAttachInterrupt(timer_tube, timer_tube_interrupt, true);
// 定时模式,单位us
timerAlarmWrite(timer_tube, 1000000, true);
// 定时模式,单位us,只触发一次
// timerAlarmWrite(timer3, 3000000, false);
}
void setTubeHardwareTimerInterrupt(int a)
{
// 清屏
for (int j=0; j<8; j++)
{
digitalWrite(pin_array[j], HIGH);
}
if (a == 1)
{
// 启动定时器
Serial.println("h1");
timerAlarmEnable(timer_tube);
}
else
{
// 关闭定时器
Serial.println("h2");
timerAlarmDisable(timer_tube);
}
}
/// 硬件定时器中断Led跑马灯(无loop)
void setLedHardwareTimerInterrupt()
{
// 设定LED的GPIO 引脚为输出模式
for(int i=0; i<size; i++)
{
pinMode(pin_list[i], OUTPUT);
}
// 初始化定时器
timer_led = timerBegin(1, 80, true);
//timer_led3 = timerBegin(2, 80, true);
// 配置周期定时器
timerAttachInterrupt(timer_led, timer_led_interrupt, true);
// 配置一次性定时器
//timerAttachInterrupt(timer_led3, timer3_led_interrupt, true);
// 定时模式,单位us
timerAlarmWrite(timer_led, 500000, true);
// 定时模式,单位us,只触发一次
//timerAlarmWrite(timer_led3, 3000000, false);
// 启动定时器
timerAlarmEnable(timer_led);
//timerAlarmEnable(timer_led3);
}
/// 软件定时器中断控制LCD(无loop)
// 初始化LCD屏幕
void setLcd()
{
// 初始化 LCD 对象
lcd.init();
// 开启背光
lcd.backlight();
lcd.setCursor(0, 0);
// 配置周期性定时器
timer_lcd.attach_ms(1000, toggle, LCD_SCL_PIN);
}
// 定义定时器中断回调函数
void toggle(int pin)
{
time_t rawtime;
struct tm *info;
char buffer[80];
time( &rawtime );
info = localtime( &rawtime );
lcd.setCursor(0, 0);
lcd.print((String)info->tm_year+"-"+info->tm_mon+"-"+info->tm_mday+" "+info->tm_hour+":"+info->tm_min+":"+info->tm_sec);
}
void execLcdSerial()
{
/// 从串口读数据
// 检测是否有串口输入
if (Serial.available()) {
// 延时以等待所有数据传输完成
delay(100);
// 清屏
//lcd.clear();
lcd.setCursor(0, 1);
// 反复读取串口的数据并在 LCD1602 屏幕上显示,直到数据读完
while (Serial.available() > 0) {
lcd.write(Serial.read());
}
}
}