/*
 * SPDX-FileCopyrightText: Copyright (c) 2020 Libre Solar Technologies GmbH
 * SPDX-FileCopyrightText: Copyright (C) Frickly Systems GmbH
 * SPDX-FileCopyrightText: Copyright (C) MBition GmbH
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/drivers/dac.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)

#if (DT_NODE_HAS_PROP(ZEPHYR_USER_NODE, dac) &&            \
     DT_NODE_HAS_PROP(ZEPHYR_USER_NODE, dac_channel_id) && \
     DT_NODE_HAS_PROP(ZEPHYR_USER_NODE, dac_resolution))
#define DAC_NODE DT_PHANDLE(ZEPHYR_USER_NODE, dac)
#define DAC_CHANNEL_ID DT_PROP(ZEPHYR_USER_NODE, dac_channel_id)
#define DAC_RESOLUTION DT_PROP(ZEPHYR_USER_NODE, dac_resolution)
#else
#error "Unsupported board: see README and check /zephyr,user node"
#define DAC_NODE DT_INVALID_NODE
#define DAC_CHANNEL_ID 0
#define DAC_RESOLUTION 0
#endif

static const struct device* const dac_dev = DEVICE_DT_GET(DAC_NODE);

static const struct dac_channel_cfg dac_ch_cfg = {
  .channel_id = DAC_CHANNEL_ID,
  .resolution = DAC_RESOLUTION,
  .buffered = true,
};

int main(void) {
  if (!device_is_ready(dac_dev)) {
    printk("DAC device %s is not ready\n", dac_dev->name);
    return 0;
  }

  int ret = dac_channel_setup(dac_dev, &dac_ch_cfg);

  if (ret != 0) {
    printk("Setting up of DAC channel failed with code %d\n", ret);
    return 0;
  }

  printk("Generating sawtooth signal at DAC channel %d.\n", DAC_CHANNEL_ID);
  while (1) {
    /* Number of valid DAC values, e.g. 4096 for 12-bit DAC */
    const int dac_values = 1U << DAC_RESOLUTION;

    /*
     * 1 msec sleep leads to about 4 sec signal period for 12-bit
     * DACs. For DACs with lower resolution, sleep time needs to
     * be increased.
     * Make sure to sleep at least 1 msec even for future 16-bit
     * DACs (lowering signal frequency).
     */
    const int sleep_time = 4096 / dac_values > 0 ? 4096 / dac_values : 1;

    for (int i = 0; i < dac_values; i++) {
      ret = dac_write_value(dac_dev, DAC_CHANNEL_ID, i);
      if (ret != 0) {
        printk("dac_write_value() failed with code %d\n", ret);
        return 0;
      }
      k_sleep(K_MSEC(sleep_time));
    }
  }
  return 0;
}
