element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
FPGA
  • Technologies
  • More
FPGA
Blog CPLD Egg Timer
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join FPGA to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: jc2048
  • Date Created: 1 Nov 2021 3:53 PM Date Created
  • Views 2487 views
  • Likes 5 likes
  • Comments 7 comments
  • cpld
  • experimenting
  • egg timer
  • vhdl
  • altera max ii
  • transistors
  • jc2048
  • logic design
Related
Recommended

CPLD Egg Timer

jc2048
jc2048
1 Nov 2021

Introduction

 

Quick little project to make an egg timer using the MAX II board that I bought over the summer.

Initially, I'll just have it counting down from three minutes. If I feel suitably inspired later, I

might extend it to allow setting the start time, have an alarm go off at the end, and stuff like

that. This is just me having a little fun and hacking some logic together. It won't be very

polished or rigourous, and I won't be bothering with simulation - I'll get it going the same way as

you would with a board full of logic chips, ie use the oscilloscope if I get stuck.

 

Hardware

 

I already had a 4-digit 7-segment display (Tru-Opto OSL40562-IY), so I'm using that. That part is

'common anode'. The anodes of the eight LEDs that make up a single digit are connected together and

brought out to a single pin on the package, so the display has four anode drive pins in total. The

cathodes are commoned, so all the 'a' segments share a single pin, all the 'b' segments have a pin,

and so on. There are actually 8 segment pins because each digit also has a decimal point. The

practical consequence of all that is that the display will need to be scanned (the drive signals

for the segments of a particular display digit will be presented whilst the common-anode connection

for the digit we are going to illuminate will be connected to the supply via a PNP transistor, and

the hardware will cycle round all four digits in turn).

 

I'm doing the connection of the anodes to the supply with some BC461 transistors, with a 1k base

resistor (I found 4 in an old antistatic bag, which was a stroke of luck). I need the transistors

because, if all the segments for a digit are on, the combined current far exceeds what it's

reasonable to take from a single IO pin on the CPLD. The segments I'll drive directly from the CPLD

with a 94R resistor [2x 47R in series] to limit the current to around 10 or 11mA.

 

This is what the top of the prototype looked like

 

image

 

The apple was a Red Windsor (and very nice it tasted, too).

 

Here's the underside

 

image

 

I simply soldered all the components in place and made the connections with wire-wrap wire.

 

Later, I added three buttons, which you'll see on the video. The buttons are to ground with a 10k

pull-up to the 3.3V supply.

 

CPLD Logic Design

 

Here's the VHDL. This is the new version with an alarm at the end of the timing period.

 

---------------------------------------------------------------
--- Filename: CPLD-egg-timer.vhd                            ---
--- Target device: EPM240T100C5                             ---
---                                                         ---
--- 3-minute egg timer                                      ---
--- scans multiplexed 7-seg display                         ---
---                                                         ---
--- Jon Clift 27th October 2021                             ---
---                                                         ---
---------------------------------------------------------------
--- Rev    Date         Comments                            ---
--- 1.0    27-Oct-21                                        ---
--- 2.0    18-Nov-21  Added alarm at end of timing          ---
---------------------------------------------------------------
library IEEE;
use IEEE.std_logic_1164.all;  
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
entity CPLD_egg_timer is  
  port (  
    clki: in  std_logic;                     --- input clock (50MHz osc).  
    button_1: in  std_logic;                 --- start/stop button  
    button_2: in  std_logic;                 --- start/stop button  
    button_3: in  std_logic;                 --- start/stop button  
    beep1: out  std_logic;                  --- alarm output  
    beep2: out  std_logic;                  --- alarm output  
    anode: out std_logic_vector(3 downto 0); --- common anode drives
    seg: out std_logic_vector(7 downto 0)  --- segment drives (7=dp, 6=g, ..., 1=b, 0=a)
    );  
end CPLD_egg_timer;  
  
architecture arch of CPLD_egg_timer is
signal prescaler1: std_logic_vector(15 downto 0);
signal prescaler2: std_logic_vector(9 downto 0);
signal digit_count: std_logic_vector(1 downto 0);
signal milli_en: std_logic := '0';
signal timer_en: std_logic := '0';
signal alarm_en: std_logic := '0';
signal second_en: std_logic := '0';
signal minutes_count: std_logic_vector(3 downto 0) := "0000";
signal tens_count: std_logic_vector(3 downto 0) := "0000";
signal seconds_count: std_logic_vector(3 downto 0) := "0000";
signal digit_value: std_logic_vector(3 downto 0);
signal debounce_1: std_logic_vector(3 downto 0) := "1111";
signal debounce_2: std_logic_vector(3 downto 0) := "1111";
signal debounce_3: std_logic_vector(3 downto 0) := "1111";
signal switch_1_state, switch_1_state_del: std_logic := '0';
signal switch_2_state: std_logic := '0';
signal switch_3_state: std_logic := '0';
begin
    clocked_stuff: process (clki)
        begin
            if (clki'event and clki='1') then
                --- prescaler down to 1ms period (divide by 50k from 50MHz clock)
          
     if (prescaler1(15 downto 0) = "1100001101001111") then --- if 49999
      prescaler1(15 downto 0) <= "0000000000000000";    ---   reset to 0
      milli_en <= '1';             ---   and set enable (for one cycle)
               else                  --- else
      prescaler1 <= prescaler1 + 1;         ---   count down
      milli_en <= '0';             ---   and keep enable low rest of time
               end if;
                --- digit scan count (modulo arithmetic: just let it wrap round)
      --- gives refresh rate of 250Hz
          
     if (milli_en = '1') then       --- if enabled, count
      digit_count <= digit_count + 1;
               end if;
     --- switch debouncing
          
     if (milli_en = '1') then
     
      debounce_1(3 downto 1) <= debounce_1(2 downto 0);
      debounce_1(0) <= button_1;
      if(debounce_1 = "1111") then
       switch_1_state <= '0';
      elsif(debounce_1 = "0000") then
       switch_1_state <= '1';
      end if;
      
      switch_1_state_del <= switch_1_state;
      
      debounce_2(3 downto 1) <= debounce_2(2 downto 0);
      debounce_2(0) <= button_2;
      if(debounce_2 = "1111") then
       switch_2_state <= '0';
      elsif(debounce_2 = "0000") then
       switch_2_state <= '1';
      end if;
      
      debounce_3(3 downto 1) <= debounce_3(2 downto 0);
      debounce_3(0) <= button_3;
      if(debounce_3 = "1111") then
       switch_3_state <= '0';
      elsif(debounce_3 = "0000") then
       switch_3_state <= '1';
      end if;
      
               end if;
     --- timer enable
          
     if (milli_en = '1') then
      if (switch_1_state = '1' and switch_1_state_del = '0') then  --- if 'start/stop' button pressed
       if (alarm_en = '1') then          --- alarm sounding
        alarm_en <= '0';            --- clear alarm
       else
        prescaler2(9 downto 0) <= "0000000000";
        tens_count(3 downto 0) <= "0000";      --- else preset time to 3 minutes
        seconds_count(3 downto 0) <= "0000";
        minutes_count(3 downto 0) <= "0011";
        timer_en <= '1';            --- and start timing
       end if;
      end if;
      if (timer_en = '1') then
       if (((tens_count(3 downto 0) = "0000") and      --- finish on 0:00
         seconds_count(3 downto 0) = "0000") and
         minutes_count(3 downto 0) = "0000") then
        timer_en <= '0';            --- disable timing
        alarm_en <= '1';            --- set alarm going
       end if;
      end if;
     end if;
     
                --- second prescaler down to 1s period (divide by 1000)
          
               if (milli_en = '1' and timer_en = '1') then     --- if enabled (once every millisecond)
      if (prescaler2(9 downto 0) = "1111100111") then   ---   if reached 999
       prescaler2(9 downto 0) <= "0000000000";      ---     reset to 0
       second_en <= '1';            ---     and set enable for one 
      else                 ---   else count down
       prescaler2 <= prescaler2 + 1;        ---     count up
       second_en <= '0';            --- keeping enable low rest of time
      end if;
               end if;
     --- the timer
       
     if (milli_en = '1' and (second_en = '1' and timer_en = '1')) then   --- if enabled
      if (seconds_count(3 downto 0) = "0000") then  ---   if seconds = 0
       seconds_count(3 downto 0) <= "1001";   ---     set back to 9
       if (tens_count(3 downto 0) = "0000") then  ---     if tens = 0
        tens_count(3 downto 0) <= "0101";   ---       set back to 5
        minutes_count <= minutes_count - 1;
       else              ---     else count down
        tens_count <= tens_count - 1;
       end if;
      else               ---   else count down
       seconds_count <= seconds_count - 1;
      end if;
               end if;
    
            end if;
         end process clocked_stuff;
 
 --- beep
  
 audio_beep: process (alarm_en,prescaler1)
  begin 
   if (alarm_en = '1') then
    beep1 <= prescaler1(15);
    beep2 <= not prescaler1(15);
   else
    beep1 <= '0';
    beep2 <= '0';
   end if;
  end process audio_beep;
 
   --- 7-segment multiplex
 seven_seg_mux: process (digit_count,minutes_count,tens_count,seconds_count)
  begin 
    case digit_count is
    when "00" => digit_value <= "0000";   --- 0
    when "01" => digit_value <= minutes_count; --- 1
    when "10" => digit_value <= tens_count;  --- 2
    when "11" => digit_value <= seconds_count; --- 3
    when others => digit_value <= "0000";  
   end case;
  end process seven_seg_mux;
  
   --- 7-segment decode (I've included the hex digits, too)
 --- seg is inverted because the segment drive is low to illuminate
 seven_seg_decode: process (digit_value)
  begin 
    case digit_value is
    when "0000" => seg <= "11000000"; --- 0
    when "0001" => seg <= "11111001"; --- 1
    when "0010" => seg <= "10100100"; --- 2
    when "0011" => seg <= "10110000"; --- 3
    when "0100" => seg <= "10011001"; --- 4
    when "0101" => seg <= "10010010"; --- 5
    when "0110" => seg <= "10000010"; --- 6
    when "0111" => seg <= "11111000"; --- 7
    when "1000" => seg <= "10000000"; --- 8
    when "1001" => seg <= "10010000"; --- 9
    when "1010" => seg <= "10001000"; --- a
    when "1011" => seg <= "10000011"; --- b
    when "1100" => seg <= "11000110"; --- c
    when "1101" => seg <= "10100001"; --- d
    when "1110" => seg <= "10000110"; --- e
    when "1111" => seg <= "10001110"; --- f
    when others => seg <= "11111111"; 
   end case;
  end process seven_seg_decode;
 --- anode drive decode
 --- anode is inverted because the anode transistor drive is low to enable
 anode_drive_decode: process (digit_count)
  begin 
    case digit_count is
    when "00" => anode <= "1110"; --- 0
    when "01" => anode <= "1101"; --- 1
    when "10" => anode <= "1011"; --- 2
    when "11" => anode <= "0111"; --- 3
    when others => anode <= "1111"; 
   end case;
  end process anode_drive_decode;
end arch; 

Hopefully that's fairly easy to understand with the comments. If anyone would like me to explain

what it's doing in more detail, I could have a go.

 

The way I've done the actual timing part is a bit clunky - the nested IFs work, but it was thrown

together quickly and it's not as 'designed' as it should be. That was the bit that didn't work when

I first tried it - I forgot to qualify the count with milli_en, so it was all over in 180ms [a very

soft and runny egg].

 

The design as it stands at the moment uses 101 out of the 240 logic elements of the device (42%),

so plenty of room to elaborate my egg timer if I want to. For any of you not used to programmable

logic, it gives an idea of the kind of complexity you can achieve with even a small, modest CPLD:

probably equivalent to a board full of logic chips, if you were to do it discretely with CMOS or

TTL logic.

 

Video

 

Here's a video of the start when I press the left button. The display goes to 3.00 and then counts

down at one second intervals.

 

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

I decided to spare you the whole 3 minutes, exciting though it was, so here's the last 15 seconds

before it finishes and sits on zero. This is the revised version with an alarm sounding at the end.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

With 10mA for the segment drive, multiplexed that gives an average of only 2.5mA, but the display

is quite visible. In real life, the contrast is much better than it appears on the video, though

obviously it would improve further with a piece of filter material in front of the display. My

decision to go fairly fast with the scanning (250 times a second) paid off with the video - I can't

see any evidence at all of strobing on the pictures.

 

If you found this interesting and would like to see other blogs I've written, a list can be found here:

jc2048 Blog Index

  • Sign in to reply

Top Comments

  • jc2048
    jc2048 over 3 years ago in reply to genebren +5
    I've modified it now to give a continuous tone at the end. That's with a piezo disc strung between two IO pins.
  • jc2048
    jc2048 over 3 years ago in reply to rsjawale24 +2
    The MAX II parts are very like a small FPGA - much more so than the older generations of CPLD parts I used to work with. This is what the 'logic element' looks like. A 4-input LUT for the combinatorial…
  • rsjawale24
    rsjawale24 over 3 years ago +1
    Great! Been a long time since I saw a CPLD based project.
  • jc2048
    jc2048 over 3 years ago in reply to genebren

    I've modified it now to give a continuous tone at the end. That's with a piezo disc strung between two IO pins.

    • Cancel
    • Vote Up +5 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 3 years ago in reply to Jan Cumps

    They've used a case statement, too, though they're starting from ASCII rather than the 4-bit binary value I'm using.

     

    Using the case is the most obvious way to do it, though there are other ways it could be expressed in VHDL. I did consider drawing the Karnaugh maps and deriving actual logic equations for myself, but in the end I was too lazy and left the synthesis to do it.

     

    With mine, since there are only 4 input bits, each segment output can be implemented as a single LUT [the LUTs on this part have 4 inputs] and the whole thing should only take up 8 LUTs, so it's quite economical in terms of CPLD resources.

    If you think about it, what's happening with this is slightly bizarre. The synthesis will take the VHDL, derive the logic equations, minimise them, then implement them each as a look-up, and hopefully end up with what I wrote in the first place sitting in the LUTs emulating the logic that it thinks I wanted.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 3 years ago

    Here's how Xess library hanles led segment displays: https://github.com/xesscorp/VHDL_Lib/blob/master/LedDigits.vhd

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 3 years ago in reply to genebren

    Indeed. I thought that when I reviewed the video - it's a real let-down at the end when nothing happens. Perhaps if I have some time tomorrow I'll do it properly.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • jc2048
    jc2048 over 3 years ago in reply to rsjawale24

    The MAX II parts are very like a small FPGA - much more so than the older generations of CPLD parts I used to work with. This is what the 'logic element' looks like.

     

    image

     

    A 4-input LUT for the combinatorial logic and a single flip-flop.

     

    It's a good way to learn basic logic design with an HDL at little cost. This board and the USB-blaster clone came to about £16 from a supplier in the UK (I could have found it a little cheaper from elsewhere, but they had stock and it was a next-day

    delivery by ordinary letter post).

     

    Although I used a CPLD here, the blog is really about the VHDL. The same design would work with any of the FPGAs that I own.

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube