#   Copyright (c) 2016-2020, Xilinx, Inc.
#
#   Redistribution and use in source and binary forms, with or without
#   modification, are permitted provided that the following conditions are met:
#
#   1.  Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#
#   2.  Redistributions in binary form must reproduce the above copyright
#       notice, this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#
#   3.  Neither the name of the copyright holder nor the names of its
#       contributors may be used to endorse or promote products derived from
#       this software without specific prior written permission.
#
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
#   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#   PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
#   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#   OR BUSINESS INTERRUPTION). HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#   ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import struct
from math import ceil
from . import Pmod
from . import MAILBOX_OFFSET

__author__ = "Graham Schelle, Giuseppe Natale, Yun Rock Qu"
__email__ = "pynq_support@xilinx.com"

def _reg2float(reg):
"""Converts 32-bit register value to floats in Python.

Parameters
----------
reg: int
A 32-bit register value read from the mailbox.

Returns
-------
float
A float number translated from the register value.

"""
s = struct.pack('>l', reg)
return round(struct.unpack('>f', s)[0], 4)

"""This class controls an Analog to Digital Converter Pmod.

AD7991. Users may configure up to 4 conversion channels at 12 bits of
resolution.

Attributes
----------
microblaze : Pmod
Microblaze processor instance used by this module.
log_running : int
The state of the log (0: stopped, 1: started).

"""

def __init__(self, mb_info):
"""Return a new instance of an ADC object.

Parameters
----------
mb_info : dict
A dictionary storing Microblaze information, such as the
IP name and the reset name.

"""
self.log_running = 0

[docs]    def reset(self):

Returns
-------
None

"""

[docs]    def read_raw(self, ch1=1, ch2=0, ch3=0):
"""Get the raw value from the Pmod ADC.

When ch1, ch2, and ch3 values are 1 then the corresponding channel
is included.

For each channel selected, this method reads and returns one sample.

Note
----
The 4th channel is not available due to the jumper (JP1) setting on

Note
----

Parameters
----------
ch1 : int
1 means include channel 1, 0 means do not include.
ch2 : int
1 means include channel 2, 0 means do not include.
ch3 : int
1 means include channel 3, 0 means do not include.

Returns
-------
list
The raw values read from the 3 channels of the Pmod ADC.

"""
if ch1 not in range(2):
raise ValueError("Valid value for ch1 is 0 or 1.")
if ch2 not in range(2):
raise ValueError("Valid value for ch2 is 0 or 1.")
if ch3 not in range(2):
raise ValueError("Valid value for ch3 is 0 or 1.")
cmd = (ch3 << 6) | (ch2 << 5) | (ch1 << 4) | READ_RAW_DATA

# Send the command
self.microblaze.write_blocking_command(cmd)

results = []
if ch1:
if ch2:
if ch3:
return results

[docs]    def read(self, ch1=1, ch2=0, ch3=0):
"""Get the voltage from the Pmod ADC.

When ch1, ch2, and ch3 values are 1 then the corresponding channel
is included.

For each channel selected, this method reads and returns one sample.

Note
----
The 4th channel is not available due to the jumper setting on ADC.

Note
----

Parameters
----------
ch1 : int
1 means include channel 1, 0 means do not include.
ch2 : int
1 means include channel 2, 0 means do not include.
ch3 : int
1 means include channel 3, 0 means do not include.

Returns
-------
list
The voltage values read from the 3 channels of the Pmod ADC.

"""
if ch1 not in range(2):
raise ValueError("Valid value for ch1 is 0 or 1.")
if ch2 not in range(2):
raise ValueError("Valid value for ch2 is 0 or 1.")
if ch3 not in range(2):
raise ValueError("Valid value for ch3 is 0 or 1.")
cmd = (ch3 << 6) | (ch2 << 5) | (ch1 << 4) | READ_VOLTAGE

# Send the command
self.microblaze.write_blocking_command(cmd)

results = []
if ch1:
if ch2:
if ch3:
return results

[docs]    def start_log_raw(self, ch1=1, ch2=0, ch3=0, log_interval_us=100):
"""Start the log of raw values with the interval specified.

This parameter log_interval_us can set the time interval between
two samples, so that users can read out multiple values in a single
log.

Parameters
----------
ch1 : int
1 means include channel 1, 0 means do not include.
ch2 : int
1 means include channel 2, 0 means do not include.
ch3 : int
1 means include channel 3, 0 means do not include.
log_interval_us : int

Returns
-------
None

"""
if log_interval_us < 0:
raise ValueError("Time between samples should be no less than 0.")
if ch1 not in range(2):
raise ValueError("Valid value for ch1 is 0 or 1.")
if ch2 not in range(2):
raise ValueError("Valid value for ch2 is 0 or 1.")
if ch3 not in range(2):
raise ValueError("Valid value for ch3 is 0 or 1.")

cmd = (ch3 << 6) | (ch2 << 5) | (ch1 << 4) | READ_AND_LOG_RAW_DATA
self.log_running = 1

self.microblaze.write_mailbox(0, log_interval_us)

# Send the command
self.microblaze.write_non_blocking_command(cmd)

[docs]    def start_log(self, ch1=1, ch2=0, ch3=0, log_interval_us=100):
"""Start the log of voltage values with the interval specified.

This parameter log_interval_us can set the time interval between
two samples, so that users can read out multiple values in a single
log.

Parameters
----------
ch1 : int
1 means include channel 1, 0 means do not include.
ch2 : int
1 means include channel 2, 0 means do not include.
ch3 : int
1 means include channel 3, 0 means do not include.
log_interval_us : int

Returns
-------
None

"""
if log_interval_us < 0:
raise ValueError("Time between samples should be no less than 0.")
if ch1 not in range(2):
raise ValueError("Valid value for ch1 is 0 or 1.")
if ch2 not in range(2):
raise ValueError("Valid value for ch2 is 0 or 1.")
if ch3 not in range(2):
raise ValueError("Valid value for ch3 is 0 or 1.")

cmd = (ch3 << 6) | (ch2 << 5) | (ch1 << 4) | READ_AND_LOG_VOLTAGE

self.log_running = 1

self.microblaze.write_mailbox(0, log_interval_us)

# Send the command
self.microblaze.write_non_blocking_command(cmd)

[docs]    def stop_log_raw(self):
"""Stop the log of raw values.

This is done by sending the reset command to IOP. There is no need to
wait for the IOP.

Returns
-------
None

"""
if self.log_running == 1:
self.log_running = 0
else:
raise RuntimeError("No grove ADC log running.")

[docs]    def stop_log(self):
"""Stop the log of voltage values.

This is done by sending the reset command to IOP. There is no need to
wait for the IOP.

Returns
-------
None

"""
if self.log_running == 1:
self.log_running = 0
else:
raise RuntimeError("No grove ADC log running.")

[docs]    def get_log_raw(self):
"""Get the log of raw values.

First stop the log before getting the log.

Returns
-------
list
List of raw samples from the ADC.

"""
# Stop logging
self.stop_log_raw()

# Prep iterators and results list

# Sweep circular buffer for samples
return None
num_words = int(ceil((tail_ptr - head_ptr) / 4))
else:

num_words = int(ceil((tail_ptr - PMOD_ADC_LOG_START) / 4))

[docs]    def get_log(self):
"""Get the log of voltage values.

First stop the log before getting the log.

Returns
-------
list
List of voltage samples from the ADC.

"""
# Stop logging
self.stop_log()

# Prep iterators and results list

# Sweep circular buffer for samples
return None