clock: add setters for period, duty cycle, pulse width

This commit is contained in:
hap 2021-09-10 12:55:25 +02:00
parent 2811cda9c6
commit c5ceada693
4 changed files with 154 additions and 30 deletions

View File

@ -1,46 +1,146 @@
// license:BSD-3-Clause
// copyright-holders:smf
// copyright-holders:smf, hap
/*
Generic clock signal device
Set the period either with device_t m_clock, or with set_period if it needs
to be more fine-tuned (m_clock has higher priority).
The duty cycle can be changed with set_duty_cycle (default is 50%), or the
pulse width (active time) can be set directly with set_pulse_width.
Output signal at machine start is right after falling edge.
TODO:
- add one-shot trigger? eg. monostable 555
*/
#include "emu.h"
#include "clock.h"
DEFINE_DEVICE_TYPE(CLOCK, clock_device, "clock", "Clock")
clock_device::clock_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, CLOCK, tag, owner, clock),
clock_device::clock_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, CLOCK, tag, owner, clock),
m_signal(0),
m_timer(nullptr),
m_output(-1),
m_duty(0.5),
m_period(attotime::never),
m_pw(attotime::never),
m_timer_init(nullptr),
m_timer_tick(nullptr),
m_signal_handler(*this)
{
}
void clock_device::device_start()
{
m_signal_handler.resolve();
m_signal_handler.resolve_safe();
save_item(NAME(m_signal));
save_item(NAME(m_output));
save_item(NAME(m_duty));
save_item(NAME(m_period));
save_item(NAME(m_pw));
save_item(NAME(m_thigh));
save_item(NAME(m_tlow));
m_timer_init = timer_alloc(TID_CLOCK_INIT);
m_timer_tick = timer_alloc(TID_CLOCK_TICK);
reinit();
}
void clock_device::device_clock_changed()
void clock_device::reinit()
{
if (!m_signal_handler.isnull() && m_clock > 0)
if (!m_timer_init)
return;
// not using synchronize(), that may retrigger more than once
m_timer_init->adjust(attotime::zero);
}
void clock_device::output()
{
if (m_signal != m_output)
{
if (m_timer == nullptr)
m_timer = timer_alloc(0);
const attotime period(attotime::from_hz(m_clock * 2));
attotime next = period - m_timer->elapsed();
if (next < attotime::zero)
next = attotime::zero;
m_timer->adjust(next, 0, period);
m_output = m_signal;
m_signal_handler(m_output);
}
else if (m_timer != nullptr)
m_timer->adjust(attotime::never);
}
void clock_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
m_signal = !m_signal;
m_signal_handler(m_signal);
switch (id)
{
case TID_CLOCK_INIT:
{
attotime period = (m_clock > 0) ? attotime::from_hz(m_clock) : m_period;
assert(!period.is_zero());
if (period.is_never())
{
m_timer_tick->adjust(attotime::never);
return;
}
if (!m_pw.is_never())
{
// set timing via pulse width
attotime pw = m_pw;
if (pw > period)
pw = period;
m_thigh = pw;
m_tlow = period - pw;
}
else
{
// set timing via duty cycle
if (m_duty == 0.5)
{
m_thigh = period / 2;
m_tlow = m_thigh;
}
else if (m_duty == 0.0)
{
m_thigh = attotime::zero;
m_tlow = period;
}
else if (m_duty == 1.0)
{
m_thigh = period;
m_tlow = attotime::zero;
}
else
{
double p = period.as_double();
m_thigh = attotime::from_double(m_duty * p);
m_tlow = attotime::from_double((1.0 - m_duty) * p);
}
}
attotime next = m_signal ? m_thigh : m_tlow;
if (next < m_timer_tick->remaining())
m_timer_tick->adjust(next);
break;
}
case TID_CLOCK_TICK:
if (m_thigh.is_zero())
m_signal = 0;
else if (m_tlow.is_zero())
m_signal = 1;
else
m_signal ^= 1;
m_timer_tick->adjust(m_signal ? m_thigh : m_tlow);
output();
break;
default:
break;
}
}

View File

@ -1,27 +1,51 @@
// license:BSD-3-Clause
// copyright-holders:smf
// copyright-holders:smf, hap
/*
Generic clock signal device
*/
#ifndef MAME_MACHINE_CLOCK_H
#define MAME_MACHINE_CLOCK_H
#pragma once
class clock_device : public device_t
{
public:
auto signal_handler() { return m_signal_handler.bind(); }
DECLARE_READ_LINE_MEMBER(signal_r) { return m_signal; }
clock_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
clock_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
auto signal_handler() { return m_signal_handler.bind(); }
auto &set_period(attotime period) { m_period = period; reinit(); return *this; }
auto &set_pulse_width(attotime pw) { assert(!pw.is_never()); m_pw = pw; reinit(); return *this; }
auto &set_duty_cycle(double duty) { assert(duty > 0.0 && duty < 1.0); m_duty = duty; m_pw = attotime::never; reinit(); return *this; }
DECLARE_READ_LINE_MEMBER(signal_r) { return m_signal; }
protected:
virtual void device_start() override;
virtual void device_reset() override { output(); }
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
virtual void device_clock_changed() override;
virtual void device_clock_changed() override { reinit(); }
private:
static constexpr device_timer_id TID_CLOCK_INIT = 0;
static constexpr device_timer_id TID_CLOCK_TICK = 1;
void reinit();
void output();
int m_signal;
emu_timer *m_timer;
int m_output;
double m_duty;
attotime m_period;
attotime m_pw;
attotime m_thigh;
attotime m_tlow;
emu_timer *m_timer_init;
emu_timer *m_timer_tick;
devcb_write_line m_signal_handler;
};

View File

@ -584,7 +584,7 @@ void card_state::brc_base(machine_config &config)
m_mcu->t0_in_cb().set(FUNC(card_state::mcu_t0_r));
// MCU T1 tied to master clock / 4
CLOCK(config, "t1_clock", 5_MHz_XTAL/4).signal_handler().set_nop();
CLOCK(config, "t1_clock", 5_MHz_XTAL/4);
m_mcu->t1_in_cb().set("t1_clock", FUNC(clock_device::signal_r)).invert();
I8243(config, m_i8243);

View File

@ -11962,7 +11962,7 @@ void tithermos_state::tithermos(machine_config &config)
m_maincpu->r().set(FUNC(tithermos_state::write_r));
m_maincpu->o().set(FUNC(tithermos_state::write_o));
CLOCK(config, "ac_line", 60).signal_handler().set_nop();
CLOCK(config, "ac_line", 60);
/* video hardware */
PWM_DISPLAY(config, m_display).set_size(4, 7);