/*
 * Copyright (c) 2015-2016, Sumio Morioka
 * All rights reserved.
 *
 * This source code was originally written by Dr.Sumio Morioka for use in the 2015-2016 issues of 
 * "the Interface magazine", published by CQ publishing Co.Ltd in Japan (http://www.cqpub.co.jp).
 * The author has no responsibility on any results caused by using this code.
 *
 * - Author: Dr.Sumio Morioka (http://www002.upp.so-net.ne.jp/morioka, FB:Sumio Morioka)
 *
 *
 * IMPORTANT NOTICE:
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * 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.
 *   * 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/hardware without specific prior written permission.
 *
 * THIS SOFTWARE/HARDWARE 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/HARDWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//////////////////////////////////////////////////////////////////////////
// sainsmart lcd controller
//////////////////////////////////////////////////////////////////////////
#include <ap_cint.h>
#include <ap_utils.h>

////////////////////////////////////////////
// port
////////////////////////////////////////////

// LCD
volatile uint1	rs		= 0;
volatile uint1	en		= 0;
volatile uint4	data	= 0;
volatile uint1	ind		= 0;

// I/O memory
volatile uint5	mem_addr	= 0;
volatile uint8	mem_din;
volatile uint1	mem_req		= 0;
volatile uint1	mem_ack;

// dummy
volatile uint1	dummy_tmr_out	= 0;

////////////////////////////////////////////
// main
////////////////////////////////////////////

void wait_tmr(uint32 tmr)	// unit clk
{
#pragma HLS INLINE OFF
	uint32	t;
	ap_wait();
	for (t = 0; t < tmr; t++) {
		dummy_tmr_out	= 1- dummy_tmr_out;
	}
	ap_wait();
}


void lcd_send_cmd(uint1 mode, uint4 wd)
{
#pragma HLS INLINE OFF
	ap_wait();
	en		= 0;
	ap_wait();
	wait_tmr(1000);

	ap_wait();
	en		= 0;
	rs		= mode;
	data	= wd;
	ap_wait();
	wait_tmr(1000);

	ap_wait();
	en		= 1;
	rs		= mode;
	data	= wd;
	ap_wait();
	wait_tmr(1000);

	ap_wait();
	en		= 0;
	rs		= mode;
	data	= wd;
	ap_wait();
	wait_tmr(1000);
}


void init_lcd()
{
#pragma HLS INLINE OFF
	wait_tmr(2000000);
	lcd_send_cmd(0, 0x3);
	wait_tmr(500000);
	lcd_send_cmd(0, 0x3);
	wait_tmr(50000);
	lcd_send_cmd(0, 0x3);
	wait_tmr(50000);

	lcd_send_cmd(0, 0x2);		// 4bit i/f
	lcd_send_cmd(0, 0x2);
	lcd_send_cmd(0, 0x8);		// 2lines, 5x8 font
	wait_tmr(10000);

	lcd_send_cmd(0, 0x0);
	lcd_send_cmd(0, 0xC);		// disp on/cursor off/blink off
	wait_tmr(10000);

	lcd_send_cmd(0, 0x0);
	lcd_send_cmd(0, 0x1);		// disp clr
	wait_tmr(200000);

	lcd_send_cmd(0, 0x0);
	lcd_send_cmd(0, 0x2);		// increment/shift off
	wait_tmr(10000);
}


uint8 read_mem(uint5 addr)
{
#pragma HLS INLINE OFF
	uint8	dt;

	ap_wait();
	mem_addr	= addr;
	mem_req	= 1;
	ap_wait();
	do {
		mem_addr	= addr;
		mem_req	= 1;
		dt	= mem_din;
	} while (mem_ack == 0);
	ap_wait();
	mem_req	= 0;
	ap_wait();

	return (dt);
}


void lcddrv()
{
#pragma HLS TOP
#pragma HLS INTERFACE ap_none port=dummy_tmr_out register

#pragma HLS INTERFACE ap_none port=rs register
#pragma HLS INTERFACE ap_none port=en register
#pragma HLS INTERFACE ap_none port=data register
#pragma HLS INTERFACE ap_none port=ind register

#pragma HLS INTERFACE ap_none port=mem_addr register
#pragma HLS INTERFACE ap_none port=mem_din
#pragma HLS INTERFACE ap_none port=mem_req register
#pragma HLS INTERFACE ap_none port=mem_ack

	uint8	pos;
	uint8	dt;

	init_lcd();

	while (1) {
		// line 1
		lcd_send_cmd(0, 0x8);
		lcd_send_cmd(0, 0x0);		// set address

		for (pos = 0; pos < 16; pos++) {
			dt	= read_mem((uint5)pos);
			lcd_send_cmd(1, (dt >> 4) & 0xF);
			lcd_send_cmd(1, dt & 0xF);
		}

		// line 2
		lcd_send_cmd(0, 0xC);
		lcd_send_cmd(0, 0x0);		// set address

		for (pos = 16; pos < 32; pos++) {
			dt	= read_mem((uint5)pos);
			lcd_send_cmd(1, (dt >> 4) & 0xF);
			lcd_send_cmd(1, dt & 0xF);
		}

		// misc
		ap_wait();
		wait_tmr(10000000);			// 100ms@100MHz
	}
}

// end of file
