Hardware components | ||||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 10 | |||
![]() |
| × | 1 | |||
| × | 2 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 5 | |||
| × | 2 | ||||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 4 | ||||
| × | 2 |
I wanted to create 7-segment display that can be used for different purposes. For example as a timer, clock or voltage/current display for DIY PSU. So LDS-3492AX (exactly like YSD-439AR6B-35 from SparkFun) 4 digits 7-segment display was chosen because it has a colon and apostrophe, so clock and temperature can be nicely displayed.
Of course, I am aware of 7-segment display controller ICs like MAX7219. But there is no easy way to use such IC for display with colon and apostrophe, especially if you want brightness control and same brightness for colon and apostrophe as for digits. So I choose to use Atmega328p chip. This will allow you to use this display as slave writing digit values via I2C or standalone as clock, with temperature sensor or anything else.
So what you see here is a schematic, PCB and example Arduino sketch of clock implementation with external RTC module. There are also Gerber files for Seeedstudio Fusion PCB service so you can easily produce PCB only for 5$ (not including shipping).
Generally PCB can be used with common anode 7-segment display as well as with common cathode 7-segment display. You only will need to use right type of transistors and connect J5 jumper accordingly.
I did mistake with buttons location. Actually I wanted them at the bottom. Anyway I will do additional version of PCB with buttons located at the right side of 7-segment displays.
I am not providing explanations other then schematic and example of code currently because I don't sure if anyone will be interested. But I will add explanation on request.
Some pictures//Global libraries
#include <TimeLib.h>
#include <Wire.h>
#include <RTClib.h>
#include <PinChangeInterrupt.h>
//Local librarues
#include "sevenseg_display_drv.h"
//Define section
#define BUTTON_PLUS 12
#define BUTTON_MINUS 11
#define BUTTON_MODE 10
#define BUTTON_SET 13
#define MAX_MODE 2
#define SECS_PER_HOUR 3600
//Top variables
sevenseg_display_drv display7seg;
double light;
volatile uint8_t mode,stopwatch_running,mode_button_pressed;
volatile unsigned long last_time_button_falling,last_time_button_rising,elapsed_millisec,stopwatch_start_millisec;
//unsigned long
RTC_DS3231 rtc;
void loop(){
switch (mode) {
case 0:
mode=1;
display7seg.clear_all();
display7seg.set_dp_bottom(2);
break;
case 1://Clock with date
show_clock_up();
show_date_down();
set_brightness();
break;
case 10://Clock with year
display7seg.clear_all();
mode=11;
break;
case 11://Clock with year
show_clock_up();
show_year_down();
set_brightness();
break;
case 20://Stop watch
mode=21;
display7seg.clear_all();
display7seg.set_colon_top();
display7seg.set_dp_bottom(2);
break;
case 21://Stop watch
stopwatch();
break;
default:
mode=0;
break;
}//switch
}
void stopwatch(){
//Stopwatch function
if(stopwatch_running==1) elapsed_millisec=millis()-stopwatch_start_millisec;
uint16_t total_sec=elapsed_millisec/1000;
uint16_t delata_millisec=elapsed_millisec%1000;
uint8_t delta_sec=total_sec%60;
uint8_t delta_min=total_sec/60%60;
uint8_t delta_hour=total_sec/60/60;
uint8_t digit;
digit=elapsed_millisec/10%10;
display7seg.set_digit_bottom(0, digit);
digit=elapsed_millisec/100%10;
display7seg.set_digit_bottom(1, digit);
digit=delta_sec%10;
display7seg.set_digit_bottom(2, digit);
digit=delta_sec/10;
display7seg.set_digit_bottom(3, digit);
digit=delta_min%10;
display7seg.set_digit_top(0, digit);
digit=delta_min/10;
display7seg.set_digit_top(1, digit);
digit=delta_hour%10;
display7seg.set_digit_top(2, digit);
digit=delta_hour/10;
display7seg.set_digit_top(3, digit);
}
void set_brightness(){
light = 0;
for (int i = 0; i < 2000; i++) {
light += analogRead(A3);
}
light = int((light/ 2000) / 1024 * 15);
display7seg.set_brightness(light);
}
void show_clock_up(){
display7seg.set_digit_top(0, minute()%10);
display7seg.set_digit_top(1, minute()/10);
display7seg.set_digit_top(2, hour()%10);
display7seg.set_digit_top(3, hour()/10);
if (millis()%1000 > 500) display7seg.set_colon_top();
else display7seg.reset_colon_top();
}
void show_date_down(){
display7seg.set_digit_bottom(0, month()%10);
display7seg.set_digit_bottom(1, month()/10);
display7seg.set_digit_bottom(2, day()%10);
display7seg.set_digit_bottom(3, day()/10);
}
void show_year_down(){
uint8_t digit;
digit=year()%10;
display7seg.set_digit_bottom(0, digit);
digit=year()/10%10;
display7seg.set_digit_bottom(1, digit);
digit=year()/100%10;
display7seg.set_digit_bottom(2, digit);
digit=year()/1000%10;
display7seg.set_digit_bottom(3, digit);
}
void print_float_num_bottom(double floatnum) {
char tempString[5];
int fourdigits;
display7seg.reset_dp_bottom(0);
display7seg.reset_dp_bottom(1);
display7seg.reset_dp_bottom(2);
display7seg.reset_dp_bottom(3);
int dig_num = log10(floatnum) + 1;
if (dig_num >= 0) {
fourdigits = round(floatnum*pow(10, 4 - dig_num));
if (fourdigits==0) sprintf(tempString, "%4s", "0000");
else sprintf(tempString, "%4d", fourdigits);
display7seg.set_digit_bottom(0, (uint8_t)(tempString[3] - '0'));
display7seg.set_digit_bottom(1, (uint8_t)(tempString[2] - '0'));
display7seg.set_digit_bottom(2, (uint8_t)(tempString[1] - '0'));
display7seg.set_digit_bottom(3, (uint8_t)(tempString[0] - '0'));
display7seg.set_dp_bottom(4 - dig_num);
}
else {
fourdigits = round(floatnum*10000);
sprintf(tempString, "%4d", fourdigits);
display7seg.set_digit_bottom(0, fourdigits % 10 );
display7seg.set_digit_bottom(1, fourdigits /10 %10);
display7seg.set_digit_bottom(2, (fourdigits /100 % 10));
display7seg.set_digit_bottom(3, 0);
display7seg.set_dp_bottom(3);
}
}
//http://forum.arduino.cc/index.php?topic=44262.0
char *ftoa(char *a, double f, int precision)
{
long p[] = { 0,10,100,1000,10000,100000,1000000,10000000,100000000 };
char *ret = a;
long heiltal = (long)f;
itoa(heiltal, a, 10);
while (*a != '\0') a++;
*a++ = '.';
long desimal = abs((long)((f - heiltal) * p[precision]));
itoa(desimal, a, 10);
return ret;
}
void plus_button(){
}
void minus_button(){
}
void set_button(){
if((millis()-last_time_button_falling)<200) return;
last_time_button_falling=millis();
switch (mode) {
case 1://Clock with date
mode=10;
break;
case 11://Clock with year
mode=0;
break;
case 21://Stop watch
switch (stopwatch_running) {
case 0://Stopwatch not running state
//Run it
stopwatch_running=1;
stopwatch_start_millisec=millis();
break;
case 1://Stopwatch running state
//Stop it
stopwatch_running=2;
break;
case 2://Display results state
//Reset it
stopwatch_running=0;
elapsed_millisec=0;
break;
default:
break;
}//switch stopwatch_running
default:
break;
}//switch mode
}
void mode_button_down(){
if((millis()-last_time_button_falling)<200) return;
last_time_button_falling=millis();
mode_button_pressed=1;
switch (mode) {
case 1://Clock with date
mode=20;
break;
case 11://Clock with year
mode=20;
break;
case 21://Stop watch
mode=0;
break;
default:
mode=0;
break;
}//switch mode
}
void mode_button_up(){
if((millis()-last_time_button_rising)<200) return;
last_time_button_rising=millis();
mode_button_pressed=0;
/*
switch (mode) {
case 1://Clock with date
break;
case 11://Clock with year
break;
case 21://Stop watch
break;
default:
break;
}//switch mode
*/
}
void setup()
{
//Serial.begin(9600);
display7seg.init();//Initiate displays
display7seg.begin_drv();//Run interupt
display7seg.set_brightness(15);
//setTime(10, 10, 0, 1, 1, 2017);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, lets set the time!");
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// January 21, 2014 at 3am you would call:
// rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
}
setSyncProvider(syncProvider); // the function to get the time from the RTC
if(timeStatus()!= timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
pinMode(A3, INPUT);//Light sensor
pinMode(BUTTON_PLUS, INPUT);
pinMode(BUTTON_MINUS, INPUT);
pinMode(BUTTON_MODE, INPUT);
pinMode(BUTTON_SET, INPUT);
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(BUTTON_PLUS), plus_button, FALLING);
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(BUTTON_MINUS), minus_button, FALLING);
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(BUTTON_MODE), mode_button_down, FALLING);
attachPinChangeInterrupt(digitalPinToPinChangeInterrupt(BUTTON_SET), set_button, FALLING);
last_time_button_falling=0;
last_time_button_rising=0;
mode = 0;
stopwatch_running=0;
mode_button_pressed=0;
}
static time_t syncProvider(){
return rtc.now().unixtime();
}
#include "sevenseg_display_drv.h"
static sevenseg_display_drv *active7segment = NULL;
sevenseg_display_drv::sevenseg_display_drv() { }
void sevenseg_display_drv::begin_drv()
{
//Start refreshing Nothing will be displayed before this function called
//From http://www.instructables.com/id/Arduino-Timer-Interrupts/
//Set timer1 - 16 bit timer
cli();// disable global interrupts
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
//Prescaller (timer speed(Hz)) = (Arduino clock speed(16MHz)) / prescaler
TCCR1B |= (1 << CS10); //No prescaller 63ns per count for 16MHz clock
//Also notice how the setups between the three timers differ slightly in the line which turns on CTC mode :
//TCCR0A |= (1 << WGM01);//for timer0
//TCCR1B |= (1 << WGM12);//for timer1
//TCCR2A |= (1 << WGM21);//for timer2
TCCR1B |= (1 << WGM12);//CTC mode
// OCR#A (the compare match value)
//remember that when you use timers 0 and 2 this number must be less than 256, and less than 65536 for timer1
OCR1A = 999;//intrupt will happen 63ns*[this value] 63usec for 999
//OCR1A = 496;//31usec
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei();//allow interrupts
}
ISR(TIMER1_COMPA_vect) {//change the 1 to 0 for timer0 and 2 for timer2
active7segment->updateDisplay();//Matrix refrshing function
}
uint8_t sevenseg_display_drv::init()
{
//Initialisation
DDRB |= B00000011;//Set pins 0,1 of port B as output
DDRD |= B11111111;//Sets pins 0 to 7 of port D as outputs, segments control
DDRC |= B00000111;//Set bits 0,1,2 of port C as output, connection to 74HC595N
PORTB &= B11111100;//Turn off colon and apostrof
PORTD = B00000000;//Turn off all segments
PORTC &= B11111000;//Shift register controls low
//Clear all bits of shift register
for (uint8_t n = 0; n < NUMOFDIGITS; n++) {
PORTC |= B00000010;//Serial clock high
PORTC &= B11111101;//Serial clock low
}
PORTC |= B00000100;//Latch shift register data
active7segment = this; // For interrupt hander
//Clear memory
clear_all();
//For pwm_count_max=15 each digit can be turn on up to 1ms out of 8 ms
//So reftesh rate something like 125Hz
//pwm_count_max = 15;//Number of brightness levels
brightness = 15;//Default value of brightness
//Initiate variables
pwm_count = 0;
digit_num = 0;
return 0;
}
void sevenseg_display_drv::clear_all(){
//Clear memory
for (uint8_t n = 0; n < NUMOFDIGITS; n++) {
dp_buffer[n] = B00000000;//Reset decimal point buffer
displaybuff[n] = B00000000;//Set all digits to off
colon_and_ap[n]= B00000000;//Clean colon and apos
}
}
void sevenseg_display_drv::clear_top(){
//Clear memory
for (uint8_t n = 4; n < NUMOFDIGITS; n++) {
dp_buffer[n] = B00000000;//Reset decimal point buffer
displaybuff[n] = B00000000;//Set all digits to off
colon_and_ap[n]= B00000000;//Clean colon and apos
}
}
void sevenseg_display_drv::clear_bottom(){
//Clear memory
for (uint8_t n = 0; n < 4; n++) {
dp_buffer[n] = B00000000;//Reset decimal point buffer
displaybuff[n] = B00000000;//Set all digits to off
colon_and_ap[n]= B00000000;//Clean colon and apos
}
}
void sevenseg_display_drv::updateDisplay()
{
/*
This function refreshing display. Called only from interupt
*/
//Debug
//PORTC |= B00000001;//Turn off all digits
if (pwm_count < PWMCOUNTMAX) {
//For brightness=0 display will be on for 1 cycle
if (pwm_count > brightness) {
PORTB &= B11111100;//Turn off colon and apostrof
PORTD = B00000000;//Turn off all segments
}
pwm_count++;//PWM counter
}
else {//End of current pwm cycle
//Here we need to update information of digit
pwm_count = 0;//restart pwm counter
//Turn off all
PORTB &= B11111100;//Turn off colon and apostrof
PORTD = B00000000;//Turn off all segments
if (digit_num < NUMOFDIGITS) {// Digits 1-7
PORTC &= B11111000;//Shift register latch data and clock signal low
PORTC |= B00000010;//Serial clock high
PORTC |= B00000101;//Latch data, serial data to high
}
else {//Digit 0
digit_num = 0;//First digit
//Turn on first digit
PORTC &= B11111001;//Shift register latch and clock signal low. Data already high vrom previous cycle
PORTC |= B00000010;//Serial clock high
PORTC |= B00000100;//Latch data
}
//Ser digit data
PORTD = displaybuff[digit_num];//Apply data for segments of current digit
PORTB |= colon_and_ap[digit_num];//Colon and apostrophe
digit_num++;//Next digit
}//end else
//*** Debug ***
//long elapsed = micros() - time_start;
//Serial.println(elapsed);
//PORTC &= B11111110;//Turn off all digits
//********************
}
//Set value of digit
void sevenseg_display_drv::set_digit( uint8_t dig_num, uint8_t value){
if(dig_num<0 || dig_num>(NUMOFDIGITS-1)) return;
displaybuff[dig_num]=pgm_read_byte_near(&bcd[value])|dp_buffer[dig_num];
}
void sevenseg_display_drv::set_digit_top( uint8_t dig_num, uint8_t value){
set_digit( dig_num+4, value);
}
void sevenseg_display_drv::set_digit_bottom( uint8_t dig_num, uint8_t value){
set_digit( 3-dig_num, value);
}
//Set brightness of all digits
void sevenseg_display_drv::set_brightness(uint8_t value) {
if (value<= PWMCOUNTMAX) brightness = value;
else brightness = PWMCOUNTMAX;
}
void sevenseg_display_drv::set_dp_top(uint8_t dig_num) {
if(dig_num<0 || dig_num>(NUMOFDIGITS-1)) return;
dp_buffer[dig_num+4] = B10000000;
displaybuff[dig_num+4] |= dp_buffer[dig_num+4];
}
void sevenseg_display_drv::set_dp_bottom(uint8_t dig_num) {
if(dig_num<0 || dig_num>(NUMOFDIGITS-1)) return;
dp_buffer[3-dig_num] = B10000000;
displaybuff[3-dig_num] |= dp_buffer[3-dig_num];
}
void sevenseg_display_drv::reset_dp_top(uint8_t dig_num) {
if(dig_num<0 || dig_num>(NUMOFDIGITS-1)) return;
dp_buffer[dig_num] = B00000000;
displaybuff[dig_num] &= B01111111;
}
void sevenseg_display_drv::reset_dp_bottom(uint8_t dig_num) {
if(dig_num<0 || dig_num>(NUMOFDIGITS-1)) return;
dp_buffer[dig_num] = B00000000;
displaybuff[dig_num] &= B01111111;
}
void sevenseg_display_drv::set_colon_top() {
for (uint8_t n = 4; n < 8; n++) {
colon_and_ap[n] |=B00000010;
}
}
void sevenseg_display_drv::set_colon_bottom() {
for (uint8_t n = 0; n < 4; n++) {
colon_and_ap[n] |=B00000010;
}
}
void sevenseg_display_drv::reset_colon_top() {
for (uint8_t n = 4; n < 8; n++) {
colon_and_ap[n] &=B11111101;
}
}
void sevenseg_display_drv::reset_colon_bottom() {
for (uint8_t n = 0; n < 4; n++) {
colon_and_ap[n] &=B11111101;
}
}
void sevenseg_display_drv::set_apostrophe_top() {
for (uint8_t n = 4; n < 8; n++) {
}
}
void sevenseg_display_drv::set_apostrophe_bottom() {
for (uint8_t n = 0; n < 4; n++) {
}
}
void sevenseg_display_drv::reset_apostrophe_top() {
for (uint8_t n = 4; n < 8; n++) {
}
}
void sevenseg_display_drv::reset_apostrophe_bottom() {
for (uint8_t n = 0; n < 4; n++) {
}
}
//Used information:
//http://www.allaboutcircuits.com/projects/interface-a-seven-segment-display-to-an-arduino/
//http://skpang.co.uk/blog/archives/323
#ifndef _SEVENSEG_DISPLAY_DRV_h
#define _SEVENSEG_DISPLAY_DRV_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "arduino.h"
#else
#include "WProgram.h"
#include "pins_arduino.h"
#endif
#define NUMOFDIGITS 8
#define PWMCOUNTMAX 15 //Number of brightness levels
//Connections:
//Port D:a-0, b-1,c-2,d-3,e-4,f-5,g-6,7-dp
//Port B 1 - col,0 - apos
//Port C 0 - SER,1 - CLK,2 - RCLK connection to 74HC595N
/*
const unsigned char PROGMEM bcd[10] = {
0xC0, //0
0xF9, //1
0xA4, //2
0xB0, //3
0x99, //4
0x92, //5
0x82, //6
0xF8, //7
0x80, //8
0x90 //9
};
*/
const unsigned char PROGMEM bcd[10] = {
~0xC0, //0
~0xF9, //1
~0xA4, //2
~0xB0, //3
~0x99, //4
~0x92, //5
~0x82, //6
~0xF8, //7
~0x80, //8
~0x90 //9
};
class sevenseg_display_drv
{
public:
sevenseg_display_drv();
void begin_drv();
void updateDisplay();
uint8_t init();
void set_digit( uint8_t dig_num, uint8_t value);
void set_digit_top( uint8_t dig_num, uint8_t value);
void set_digit_bottom( uint8_t dig_num, uint8_t value);
void set_brightness(uint8_t brightness);
void set_dp_top(uint8_t dig_num );
void set_dp_bottom(uint8_t dig_num );
void reset_dp_top(uint8_t dig_num);
void reset_dp_bottom(uint8_t dig_num);
void set_colon_top();
void reset_colon_top();
void set_apostrophe_top();
void reset_apostrophe_top();
void set_colon_bottom();
void reset_colon_bottom();
void set_apostrophe_bottom();
void reset_apostrophe_bottom();
void clear_all();
void clear_top();
void clear_bottom();
private:
uint8_t displaybuff[8],dp_buffer[8],colon_and_ap[8];//Supports up to 2 4 digit 7 segment modules
volatile uint8_t pwm_count,brightness, digit_num,display_num, pwm_count_max;
};
#endif
Comments
Please log in or sign up to comment.