Page 1 of 1

Tiny6410 Spi Interface

Posted: Thu Aug 31, 2017 5:02 am
by Nagarjuna
Hello, Friends

I am using linux kernel version as linux-2.6.38 ,I want to interface mini6410 spi with DAC AD5689R which is spi compatible.

here are the steps i have did.

Step1. Go to linux-2.6.38 execute make menuconfig In that Device Drivers-->SPI support--> --- SPI support
[*] Debug support for SPI drivers
*** SPI Master Controller Drivers ***
-*- Utilities for Bitbanging SPI masters
<*> GPIO-based bitbanging SPI Master
<*> Samsung S3C64XX series type SPI
< > Xilinx SPI controller common module
< > DesignWare SPI controller core support
*** SPI Protocol Masters ***
<*> User mode SPI device driver support
< > Infineon TLE62X0 (for power switching)
Step2. Edit the board file patch to add spi slave information into arch/arm/mach-s3c64xx/mach-mini6410.c

Here i am using GPC0,GPC1,GPC2,GPC3 Pins as SPIMIS0[0],SPICLK[0],SPIMOSI[0],SPICSn[0]

SPIMISO[0] is connected to SDIN of DAC AD5689R
SPICLK[0] is connected to SCLK of DAC AD5689R
SPICSn[0] is connected to SYNC of DAC AD5689R

here is my arch/arm/mach-s3c64xx/mach-mini6410.c file for SPI

/* linux/arch/arm/mach-s3c64xx/mach-mini6410.c
*
* Copyright 2010 FriendlyARM (www.arm9.net)
*
* Copyright 2008 Openmoko, Inc.
* Copyright 2008 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/leds.h>
#include <linux/fb.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/dm9000.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/proc_fs.h>

#include <video/platform_lcd.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>

#include <mach/hardware.h>
#include <mach/regs-fb.h>
#include <mach/map.h>

#include <asm/irq.h>
#include <asm/mach-types.h>

#include <plat/regs-serial.h>
#include <mach/gpio-bank-c.h>
#include <mach/spi-clocks.h>
#include <mach/regs-modem.h>
#include <mach/regs-gpio.h>
#include <mach/regs-sys.h>
#include <mach/regs-srom.h>
#include <plat/iic.h>
#include <plat/fb.h>
#include <plat/gpio-cfg.h>
#include <plat/nand.h>

#include <mach/s3c6410.h>
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
#include <plat/adc.h>
#include <mach/ts.h>
#include <plat/regs-usb-hsotg-phy.h>
#include <plat/audio.h>
#include <plat/fimc.h>

#include <linux/mmc/host.h>
#include <plat/sdhci.h>
#include <linux/spi/spidev.h>
#include <linux/spi/spi.h>
#include <plat/s3c64xx-spi.h>

#define UCON S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK
#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE

extern void s3c64xx_reserve_bootmem(void);
extern int s3c_media_read_proc(char *buf, char **start, off_t offset,
int count, int *eof, void *data);


static struct s3c2410_uartcfg mini6410_uartcfgs[] __initdata = {
[0] = {
.hwport = 0,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
},
[1] = {
.hwport = 1,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
},
[2] = {
.hwport = 2,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
},
[3] = {
.hwport = 3,
.flags = 0,
.ucon = UCON,
.ulcon = ULCON,
.ufcon = UFCON,
},
};

/* framebuffer and LCD setup. */

/* GPF15 = LCD backlight control
* GPF13 => Panel power
* GPN5 = LCD nRESET signal
* PWM_TOUT1 => backlight brightness
*/

static void mini6410_lcd_power_set(struct plat_lcd_data *pd,
unsigned int power)
{
if (power) {
gpio_direction_output(S3C64XX_GPF(13), 1);
gpio_direction_output(S3C64XX_GPF(15), 1);

/* fire nRESET on power up */
gpio_direction_output(S3C64XX_GPN(5), 0);
msleep(10);
gpio_direction_output(S3C64XX_GPN(5), 1);
msleep(1);
} else {
gpio_direction_output(S3C64XX_GPF(15), 0);
gpio_direction_output(S3C64XX_GPF(13), 0);
}
}

static struct plat_lcd_data mini6410_lcd_power_data = {
.set_power = mini6410_lcd_power_set,
};

static void cs_set_level(unsigned line_id, int lvl) 
{  
gpio_direction_output(line_id, lvl);  
}  

static struct s3c64xx_spi_csinfo s3c64xx_spi0_csinfo = {  
.fb_delay=100,  
 .line=S3C64XX_GPC(3),  
.set_level=cs_set_level,  
};  

static struct spi_board_info s3c6410_spi0_board[] = {  
[0] = {  
.modalias = "spidev",  
.bus_num= 0,  
.chip_select= 0,  
.irq =IRQ_SPI0,  
.max_speed_hz= 500*10000,  //50Mhz speed
.mode=SPI_MODE_0,  
.controller_data=&s3c64xx_spi0_csinfo, 
.platform_data=&s3c6410_spi0_platdata; 
.gpio_setup=&S3C64XX_spi_gpiocfg_bus0_GPC0_1_2_3,
},  
};  

static struct s3c64xx_spi_csinfo s3c64xx_spi1_csinfo = {  
  .fb_delay=100,  
 .line=S3C64XX_GPC(7),  
 .set_level=cs_set_level,  
}; 

static struct spi_board_info s3c6410_spi1_board[] = {  
[0] = {  
.modalias = "spidev",  
.bus_num= 1,  
.chip_select= 0,  
.irq = IRQ_SPI1,  
.max_speed_hz = 500*10000,  //50Mhz speed
.mode=SPI_MODE_0,  
.controller_data=&s3c64xx_spi1_csinfo, 
.platform_data=&s3c6410_spi1_platdata;
.gpio_setup=&S3C64XX_spi_gpiocfg_bus1_GPC4_5_6_7,
},  
};  

 


static struct platform_device mini6410_lcd_powerdev = {
.name = "platform-lcd",
.dev.parent = &s3c_device_fb.dev,
.dev.platform_data = &mini6410_lcd_power_data,
};

static struct s3c_fb_pd_win mini6410_fb_win0 = {
/* this is to ensure we use win0 */
.win_mode = {
#if 0
.pixclock = 115440,
#endif
.left_margin = 0x03,
.right_margin = 0x02,
.upper_margin = 0x01,
.lower_margin = 0x01,
.hsync_len = 0x28,
.vsync_len = 0x01,
.xres = 480,
.yres = 272,
},
.max_bpp = 32,
.default_bpp = 16,
};

/* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
static struct s3c_fb_platdata mini6410_lcd_pdata __initdata = {
.setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
.win[0] = &mini6410_fb_win0,
.vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
.vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
};

/* MMC/SD config */
static struct s3c_sdhci_platdata mini6410_hsmmc0_pdata = {
.max_width = 4,
.cd_type = S3C_SDHCI_CD_INTERNAL,
};

static struct s3c_sdhci_platdata mini6410_hsmmc1_pdata = {
.max_width = 4,
.cd_type = S3C_SDHCI_CD_PERMANENT,
};

/* Nand flash */
struct mtd_partition mini6410_nand_part[] = {
{
.name = "Bootloader",
.offset = 0,
.size = (4 * 128 *SZ_1K),
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = "Kernel",
.offset = (4 * 128 *SZ_1K),
.size = (5*SZ_1M) ,
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = "File System",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};

static struct s3c2410_nand_set mini6410_nand_sets[] = {
[0] = {
.name = "nand",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(mini6410_nand_part),
.partitions = mini6410_nand_part,
},
};

static struct s3c2410_platform_nand mini6410_nand_info = {
.tacls = 25,
.twrph0 = 55,
.twrph1 = 40,
.nr_sets = ARRAY_SIZE(mini6410_nand_sets),
.sets = mini6410_nand_sets,
};

#ifdef CONFIG_USB_SUPPORT
/* Initializes OTG Phy. to output 48M clock */
void s3c_otg_phy_config(int enable) {
u32 val;

if (enable) {
__raw_writel(0x0, S3C_PHYPWR); /* Power up */

val = __raw_readl(S3C_PHYCLK);
val &= ~S3C_PHYCLK_CLKSEL_MASK;
__raw_writel(val, S3C_PHYCLK);

__raw_writel(0x1, S3C_RSTCON);
udelay(5);
__raw_writel(0x0, S3C_RSTCON); /* Finish the reset */
udelay(5);
} else {
__raw_writel(0x19, S3C_PHYPWR); /* Power down */
}
}
EXPORT_SYMBOL(s3c_otg_phy_config);
#endif

/* Ethernet */
#ifdef CONFIG_DM9000
#define S3C64XX_PA_DM9000 (0x18000000)
#define S3C64XX_SZ_DM9000 SZ_1M
#define S3C64XX_VA_DM9000 S3C_ADDR(0x03b00300)

static struct resource dm9000_resources[] = {
[0] = {
.start = S3C64XX_PA_DM9000,
.end = S3C64XX_PA_DM9000 + 3,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = S3C64XX_PA_DM9000 + 4,
.end = S3C64XX_PA_DM9000 + S3C64XX_SZ_DM9000 - 1,
.flags = IORESOURCE_MEM,
},
[2] = {
.start = IRQ_EINT(7),
.end = IRQ_EINT(7),
.flags = IORESOURCE_IRQ | IRQF_TRIGGER_HIGH,
},
};

static struct dm9000_plat_data dm9000_setup = {
.flags = DM9000_PLATF_16BITONLY,
.dev_addr = { 0x08, 0x90, 0x00, 0xa0, 0x90, 0x90 },
};

static struct platform_device s3c_device_dm9000 = {
.name = "dm9000",
.id = 0,
.num_resources = ARRAY_SIZE(dm9000_resources),
.resource = dm9000_resources,
.dev = {
.platform_data = &dm9000_setup,
}
};

static int __init dm9000_set_mac(char *str) {
unsigned char addr[6];
unsigned int val;
int idx = 0;
char *p = str, *end;

while (*p && idx < 6) {
val = simple_strtoul(p, &end, 16);
if (end <= p) {
/* convert failed */
break;
} else {
addr[idx++] = val;
p = end;
if (*p == ':'|| *p == '-') {
p++;
} else {
break;
}
}
}

if (idx == 6) {
printk("Setup ethernet address to %pM\n", addr);
memcpy(dm9000_setup.param_addr, addr, 6);
}

return 1;
}

__setup("ethmac=", dm9000_set_mac);
#endif

static struct map_desc mini6410_iodesc[] = {
{
/* LCD support */
.virtual = (unsigned long)S3C_VA_LCD,
.pfn = __phys_to_pfn(S3C_PA_FB),
.length = SZ_16K,
.type = MT_DEVICE,
},
#ifdef CONFIG_DM9000
{
.virtual = (u32)S3C64XX_VA_DM9000,
.pfn = __phys_to_pfn(S3C64XX_PA_DM9000),
.length = S3C64XX_SZ_DM9000,
.type = MT_DEVICE,
},
#endif
};

static struct platform_device *mini6410_devices[] __initdata = {
#ifdef CONFIG_MINI6410_SD_CH0
&s3c_device_hsmmc0,
#endif
#ifdef CONFIG_MINI6410_SD_CH1
&s3c_device_hsmmc1,
#endif
&s3c_device_i2c0,
#ifdef CONFIG_S3C_DEV_I2C1
&s3c_device_i2c1,
#endif
&s3c_device_nand,
&s3c_device_fb,
&s3c_device_ohci,
&s3c_device_usb_hsotg,
#if defined(CONFIG_SND_SAMSUNG_AC97) || defined(CONFIG_SND_SAMSUNG_AC97_MODULE)
&s3c64xx_device_ac97,
#endif
&s3c64xx_device_iis0,
&samsung_asoc_dma,

&mini6410_lcd_powerdev,

#ifdef CONFIG_DM9000
&s3c_device_dm9000,
#endif
#ifdef CONFIG_S3C_ADC
&s3c_device_adc,
#endif
#if defined(CONFIG_TOUCHSCREEN_MINI6410) || defined(CONFIG_SAMSUNG_DEV_TS)
&s3c_device_ts,
#endif
&s3c_device_wdt,
#ifdef CONFIG_S3C_DEV_RTC
&s3c_device_rtc,
#endif

/* Multimedia support */
#ifdef CONFIG_VIDEO_SAMSUNG
&s3c_device_vpp,
&s3c_device_mfc,
&s3c_device_tvenc,
&s3c_device_tvscaler,
&s3c_device_rotator,
&s3c_device_jpeg,
&s3c_device_fimc0,
&s3c_device_fimc1,
&s3c_device_g2d,
&s3c_device_g3d,
#endif

&s3c64xx_device_spi0,
&s3c64xx_device_spi1,
};

#ifdef CONFIG_SND_SOC_TINY6410_WM8960
#include <sound/wm8960.h>
static struct wm8960_data wm8960_pdata = {
.capless = 0,
.dres = WM8960_DRES_400R,
};
#endif

static struct i2c_board_info i2c_devs0[] __initdata = {
#ifdef CONFIG_SND_SOC_TINY6410_WM8960
{
I2C_BOARD_INFO("wm8960", 0x1a),
.platform_data = &wm8960_pdata,
},
#endif
{ I2C_BOARD_INFO("ov965x", 0x30), },
};

static struct i2c_board_info i2c_devs1[] __initdata = {
/* Add your i2c device here */
};

#ifdef CONFIG_SAMSUNG_DEV_TS
static struct s3c2410_ts_mach_info s3c_ts_platform __initdata = {
.delay = 10000,
.presc = 49,
.oversampling_shift = 2,
};
#endif

#ifdef CONFIG_TOUCHSCREEN_MINI6410
static struct s3c_ts_mach_info s3c_ts_platform __initdata = {
.delay = 0xFFFF,
.presc = 0xFF,
.oversampling_shift = 2,
.resol_bit = 12,
.s3c_adc_con = ADC_TYPE_2,
};
#endif

static void __init mini6410_map_io(void)
{
u32 tmp;

s3c64xx_init_io(mini6410_iodesc, ARRAY_SIZE(mini6410_iodesc));
s3c24xx_init_clocks(12000000);
s3c24xx_init_uarts(mini6410_uartcfgs, ARRAY_SIZE(mini6410_uartcfgs));

/* set the LCD type */

tmp = __raw_readl(S3C64XX_SPCON);
tmp &= ~S3C64XX_SPCON_LCD_SEL_MASK;
tmp |= S3C64XX_SPCON_LCD_SEL_RGB;
__raw_writel(tmp, S3C64XX_SPCON);

/* remove the lcd bypass */
tmp = __raw_readl(S3C64XX_MODEM_MIFPCON);
tmp &= ~MIFPCON_LCD_BYPASS;
__raw_writel(tmp, S3C64XX_MODEM_MIFPCON);

#ifdef CONFIG_VIDEO_SAMSUNG
s3c64xx_reserve_bootmem();
#endif
}

static void __init mini6410_machine_init(void)
{
u32 cs1;

s3c_i2c0_set_platdata(NULL);
#ifdef CONFIG_S3C_DEV_I2C1
s3c_i2c1_set_platdata(NULL);
#endif

s3c_fb_set_platdata(&mini6410_lcd_pdata);

#ifdef CONFIG_SAMSUNG_DEV_TS
s3c24xx_ts_set_platdata(&s3c_ts_platform);
#endif
#ifdef CONFIG_TOUCHSCREEN_MINI6410
s3c_ts_set_platdata(&s3c_ts_platform);
#endif

s3c_sdhci0_set_platdata(&mini6410_hsmmc0_pdata);
s3c_sdhci1_set_platdata(&mini6410_hsmmc1_pdata);

#ifdef CONFIG_MTD_NAND_S3C
s3c_device_nand.name = "s3c6410-nand";
#endif
s3c_nand_set_platdata(&mini6410_nand_info);

s3c64xx_ac97_setup_gpio(0);

/* configure nCS1 width to 16 bits */

cs1 = __raw_readl(S3C64XX_SROM_BW) &
~(S3C64XX_SROM_BW__CS_MASK << S3C64XX_SROM_BW__NCS1__SHIFT);
cs1 |= ((1 << S3C64XX_SROM_BW__DATAWIDTH__SHIFT) |
(1 << S3C64XX_SROM_BW__WAITENABLE__SHIFT) |
(1 << S3C64XX_SROM_BW__BYTEENABLE__SHIFT)) <<
S3C64XX_SROM_BW__NCS1__SHIFT;
__raw_writel(cs1, S3C64XX_SROM_BW);

/* set timing for nCS1 suitable for ethernet chip */

__raw_writel((0 << S3C64XX_SROM_BCX__PMC__SHIFT) |
(6 << S3C64XX_SROM_BCX__TACP__SHIFT) |
(4 << S3C64XX_SROM_BCX__TCAH__SHIFT) |
(1 << S3C64XX_SROM_BCX__TCOH__SHIFT) |
(0xe << S3C64XX_SROM_BCX__TACC__SHIFT) |
(4 << S3C64XX_SROM_BCX__TCOS__SHIFT) |
(0 << S3C64XX_SROM_BCX__TACS__SHIFT), S3C64XX_SROM_BC1);

gpio_request(S3C64XX_GPN(5), "LCD power");
gpio_request(S3C64XX_GPF(13), "LCD power");
gpio_request(S3C64XX_GPF(15), "LCD power");

if (ARRAY_SIZE(i2c_devs0)) {
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
}
if (ARRAY_SIZE(i2c_devs1)) {
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
}

/* setup SPI0 Pins */

s3c_gpio_cfgpin(S3C64XX_GPC(0),S3C64XX_GPC0_SPI_MISO0);
s3c_gpio_cfgpin(S3C64XX_GPC(1),S3C64XX_GPC0_SPI_CLK0);
s3c_gpio_cfgpin(S3C64XX_GPC(2),S3C64XX_GPC0_SPI_MOSI0);

/* First Spi Driver Instanace */

s3c_gpio_cfgpin(S3C64XX_GPC(3),S3C_GPIO_OUTPUT);

s3c_device_spi0.dev.platform_data=&s3c6410_spi0_platdata;
  
spi_register_board_info(s3c6410_spi0_board, ARRAY_SIZE(s3c6410_spi0_board));  

//Second SPI Driver Instance

s3c6410_gpio_cfgpin(S3C64XX_GPC(7),S3C_GPIO_OUTPUT);

s3c_device_spi1.dev.platform_data=&s3c6410_spi1_platdata;

spi_register_board_info(s3c6410_spi1_board, ARRAY_SIZE(s3c6410_spi1_board))


#ifdef CONFIG_S3C64XX_DEV_FIMC0
s3c_fimc0_set_platdata(NULL);
#endif
#ifdef CONFIG_S3C64XX_DEV_FIMC1
s3c_fimc1_set_platdata(NULL);
#endif

platform_add_devices(mini6410_devices, ARRAY_SIZE(mini6410_devices));

#ifdef CONFIG_VIDEO_SAMSUNG
create_proc_read_entry("videomem", 0, NULL, s3c_media_read_proc, NULL);
#endif
}

MACHINE_START(MINI6410, "MINI6410")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.boot_params = S3C64XX_PA_SDRAM + 0x100,

.init_irq = s3c6410_init_irq,
.map_io = mini6410_map_io,
.init_machine = mini6410_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END

Step3: In arch/arm/mach-s3c64xx/Kconfig
add:(otherwise dev-spi.c don`t compiling)
config S3C64XX_DEV_SPI
bool
default y
help
Setup spi bus.

step4:Makefile
# device support
obj-y += dev-uart.o
obj-y += dev-audio.o
obj-y += dev-spi.o


Step5: Recompile the kernel by executing make zImage in linux-2.6.38 directory

step6: Flash the zImage into the target board

step7: in /dev we wil find two device files named as spidev0.0 and spidev1.0

step8: In user mode i opened the device file spidev0.0 and start communicating with slave device ad5689r dac

 pin connections betwen mini6410 and dac ad5689r are as folows


Here i am using GPC0,GPC1,GPC2,GPC3 Pins as SPIMIS0[0],SPICLK[0],SPIMOSI[0],SPICSn[0]

SPIMISO[0] is connected to SDIN of DAC AD5689R
SPICLK[0] is connected to SCLK of DAC AD5689R
SPICSn[0] is connected to SYNC of DAC AD5689R

here is user application
/**
* @file spi_ad5689r.c
*
*
* @brief Uses serbus to control an AD5689R DAC.
*
* Requires an SPI Kernel driver be loaded to expose a /dev/spidevX.Y
* interface and an AD5689R be connected on the SPI bus.
*/

#include "spidriver.h"
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>

#define AD5689R_BUS 0 // Connected to /dev/spidev1.X bus
#define AD5689R_CS 0 // Using chip select 0 (/dev/spidev1.0)
#define AD5689R_FREQ 50000000 // SPI clock frequency in Hz
#define AD5689R_BITS 8 // SPI bits per word
#define AD5689R_CLOCKMODE 0 // SPI clock mode

static uint8_t running;

int spidev_fd;
/**
* @brief Sets the given SPI bus per the AD5689r's required configuration
*
* @param spi_fd SPI bus file descriptor
*/
void AD5689R_SPIConfig(int spidev_fd)
{
SPI_setMaxFrequency(spidev_fd, AD5689R_FREQ);
SPI_setBitsPerWord(spidev_fd, AD5689R_BITS);
SPI_setClockMode(spidev_fd, AD5689R_CLOCKMODE);
SPI_setCSActiveHigh(spidev_fd);
SPI_setBitOrder(spidev_fd, SPI_MSBFIRST);
}



/**
* @brief Called when Ctrl+C is pressed - triggers the program to stop.
*/


int main()

{



unsigned char buf[3]={0x39,0xFF,0xFF};

// Open the SPI device file:

spidev_fd = SPI_open(AD5689R_BUS, AD5689R_CS);

if (spidev_fd < 0) {
printf("*Could not open SPI bus %d\n", AD5689R_BUS);
exit(0);
}

// Configure the SPI bus:
AD5689R_SPIConfig(spidev_fd);


// Loop until Ctrl+C pressed:
running = 1;

while(running)
{

SPI_setCSActiveHigh(spidev_fd);

SPI_write(spidev_fd,buf, sizeof(buf));



SPI_setCSActiveLow(spidev_fd);



}

SPI_close(spidev_fd);
return 0;
}
/*******************************************************************************
* Copyright (c) 2015 - Gray Cat Labs - https://graycat.io
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/

/**
* @file spidriver.c
* @author Alex Hiam - <alex@graycat.io>
*
* @brief A basic driver for controlling Linux spidev interfaces.
*
* Requires an SPI Kernel driver be loaded to expose /dev/spidevX.Y interfaces
* which provide the standard Linux SPI ioctls. This driver is really just an
* ioctl wrapper.
*/

#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include "spidriver.h"

/// Buffer size at least large enough to fit the max length of "/dev/spidevX.Y"
#define SPIDEV_PATH_LEN 300
/// Maximum transfer size set to standard page size of 4096 bytes
#define MAX_TRANSFER_SIZE 4096

int SPI_open(uint8_t bus, uint8_t cs)

{
char device[SPIDEV_PATH_LEN];
sprintf(device, "/dev/spidev%d.%d", bus, cs);
return open(device, O_RDWR, 0);
}


void SPI_close(int spidev_fd)

{
close(spidev_fd);

}

int SPI_read(int spidev_fd, void *rx_buffer, int n_words)

{
uint8_t bits_per_word;
uint32_t n_bytes;
struct spi_ioc_transfer transfer;
bits_per_word = SPI_getBitsPerWord(spidev_fd);
if (bits_per_word < 0) return bits_per_word;
// Round up to the next biggest number of bytes:
n_bytes = (uint32_t) (((float) (bits_per_word * n_words)) / 8.0 + 0.5);
if (!n_bytes) return 0;
if (n_bytes > MAX_TRANSFER_SIZE) n_bytes = MAX_TRANSFER_SIZE;

memset((void *) &transfer, 0, sizeof(struct spi_ioc_transfer));
transfer.tx_buf = 0;
transfer.rx_buf = (uintptr_t) rx_buffer;
transfer.len = n_bytes;
transfer.speed_hz = 0;
transfer.delay_usecs = 0;
transfer.bits_per_word = bits_per_word;
transfer.cs_change = 0;
if (ioctl(spidev_fd, SPI_IOC_MESSAGE(1), &transfer) < 0) return -1;
return (n_bytes<<3) / bits_per_word;
}

int SPI_write(int spidev_fd, void *tx_buffer, int n_words)

{
uint8_t bits_per_word;
uint32_t n_bytes;
struct spi_ioc_transfer transfer;
bits_per_word = SPI_getBitsPerWord(spidev_fd);

if (bits_per_word < 0)

return bits_per_word;

n_bytes = (uint32_t) (((float) (bits_per_word * n_words)) / 8.0 + 0.5);

if (!n_bytes)
return 0;

if (n_bytes > MAX_TRANSFER_SIZE) n_bytes = MAX_TRANSFER_SIZE;

memset((void *) &transfer, 0, sizeof(struct spi_ioc_transfer));
transfer.tx_buf = (uintptr_t) tx_buffer;
transfer.rx_buf = 0;
transfer.len = n_bytes;
transfer.speed_hz = 0;
transfer.delay_usecs = 0;
transfer.bits_per_word = bits_per_word;
transfer.cs_change = 0;
if (ioctl(spidev_fd, SPI_IOC_MESSAGE(1), &transfer) < 0) return -1;
return (n_bytes<<3) / bits_per_word;
}

int SPI_transaction(int spidev_fd, void *tx_buffer, int n_tx_words,
void *rx_buffer, int n_rx_words) {
uint8_t bits_per_word;
uint32_t n_tx_bytes, n_rx_bytes;
struct spi_ioc_transfer transfers[2];
int n_transfers;
bits_per_word = SPI_getBitsPerWord(spidev_fd);
if (bits_per_word < 0)
return bits_per_word;
// Round up to the next biggest number of bytes:
n_tx_bytes = (uint32_t) (((float) (bits_per_word * n_tx_words)) / 8.0 + 0.5);
n_rx_bytes = (uint32_t) (((float) (bits_per_word * n_rx_words)) / 8.0 + 0.5);
if (!n_rx_bytes && !n_tx_bytes) return 0;
if (n_rx_bytes > MAX_TRANSFER_SIZE) n_rx_bytes = MAX_TRANSFER_SIZE;
if (n_tx_bytes > MAX_TRANSFER_SIZE) n_tx_bytes = MAX_TRANSFER_SIZE;

n_transfers = 0;
if (n_tx_bytes) {
memset((void *) &transfers[n_transfers], 0,
sizeof(struct spi_ioc_transfer));

transfers[n_transfers].tx_buf = (uintptr_t) tx_buffer;
transfers[n_transfers].rx_buf = 0;
transfers[n_transfers].len = n_tx_bytes;
transfers[n_transfers].speed_hz = 0;
transfers[n_transfers].delay_usecs = 0;
transfers[n_transfers].bits_per_word = bits_per_word;
transfers[n_transfers].cs_change = 0;

++n_transfers;
}

if (n_rx_bytes) {
memset((void *) &transfers[n_transfers], 0,
sizeof(struct spi_ioc_transfer));

transfers[n_transfers].tx_buf = 0;
transfers[n_transfers].rx_buf = (uintptr_t) rx_buffer;
transfers[n_transfers].len = n_rx_bytes + 1;
transfers[n_transfers].speed_hz = 0;
transfers[n_transfers].delay_usecs = 0;
transfers[n_transfers].bits_per_word = bits_per_word;
transfers[n_transfers].cs_change = 0;

++n_transfers;
}

if (ioctl(spidev_fd, SPI_IOC_MESSAGE(n_transfers), transfers) < 0) return -1;
return (n_rx_bytes << 3) / bits_per_word;
}

int SPI_transfer(int spidev_fd, void *tx_buffer, void *rx_buffer, int n_words) {
uint8_t bits_per_word;
uint32_t n_bytes;
struct spi_ioc_transfer transfer;
bits_per_word = SPI_getBitsPerWord(spidev_fd);
if (bits_per_word < 0)
return bits_per_word;
n_bytes = (uint32_t) (((float) (bits_per_word * n_words)) / 8.0 + 0.5);
if (!n_bytes) return 0;
if (n_bytes > MAX_TRANSFER_SIZE) n_bytes = MAX_TRANSFER_SIZE;

memset((void *) &transfer, 0, sizeof(struct spi_ioc_transfer));
transfer.tx_buf = (uintptr_t) tx_buffer;
transfer.rx_buf = (uintptr_t) rx_buffer;
transfer.len = n_bytes;
transfer.speed_hz = 0;
transfer.delay_usecs = 0;
transfer.bits_per_word = bits_per_word;
transfer.cs_change = 0;
if (ioctl(spidev_fd, SPI_IOC_MESSAGE(1), &transfer) < 0) return -1;
return (n_bytes<<3) / bits_per_word;
}

int SPI_setBitOrder(int spidev_fd, SPI_bit_order bit_order) {
uint8_t order = (uint8_t) bit_order; // Just to be safe
if (ioctl(spidev_fd, SPI_IOC_WR_LSB_FIRST, &order) < 0) return -1;
return 0;
}

int SPI_setBitsPerWord(int spidev_fd, uint8_t bits_per_word) {
if (ioctl(spidev_fd, SPI_IOC_WR_BITS_PER_WORD, &bits_per_word) < 0) {
return -1;
}
return 0;
}

int SPI_getBitsPerWord(int spidev_fd) {
uint8_t bits_per_word;
if (ioctl(spidev_fd, SPI_IOC_RD_BITS_PER_WORD, &bits_per_word) < 0) {
return -1;
}
return bits_per_word == 0 ? 8 : bits_per_word;
}

int SPI_setMaxFrequency(int spidev_fd, uint32_t frequency) {
if (ioctl(spidev_fd, SPI_IOC_WR_MAX_SPEED_HZ, &frequency) < 0) return -1;
return 0;
}

int SPI_getMaxFrequency(int spidev_fd) {
uint32_t frequency;
if (ioctl(spidev_fd, SPI_IOC_RD_MAX_SPEED_HZ, &frequency) < 0) return -1;
return frequency;
}

int SPI_setClockMode(int spidev_fd, uint8_t clock_mode) {
uint8_t mode;
mode = SPI_getMode(spidev_fd);
if (mode < 0) return mode;
mode &= ~0x3;
mode |= clock_mode & 0x3;
return SPI_setMode(spidev_fd, mode);
}

int SPI_getClockMode(int spidev_fd) {
uint8_t clock_mode;
clock_mode = SPI_getMode(spidev_fd);
if (clock_mode < 0) return clock_mode;
return clock_mode & 0x3;
}

int SPI_setCSActiveLow(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode & ~SPI_CS_HIGH);
}

int SPI_setCSActiveHigh(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode | SPI_CS_HIGH);
}

int SPI_enableCS(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode & ~SPI_NO_CS);
}

int SPI_disableCS(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode | SPI_NO_CS);
}

int SPI_enableLoopback(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode | SPI_LOOP);
}

int SPI_disableLoopback(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode & ~SPI_LOOP);
}

int SPI_enable3Wire(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode | SPI_3WIRE);
}

int SPI_disable3Wire(int spidev_fd) {
int mode = SPI_getMode(spidev_fd);
if (mode < 0) return -1;
return SPI_setMode(spidev_fd, mode & ~SPI_3WIRE);
}

int SPI_setMode(int spidev_fd, uint8_t mode) {
if (ioctl(spidev_fd, SPI_IOC_WR_MODE, &mode) < 0) return -1;
return 0;
}

int SPI_getMode(int spidev_fd) {
uint8_t mode;
if (ioctl(spidev_fd, SPI_IOC_RD_MODE, &mode) < 0) return -1;
return mode;
}

/include
/*******************************************************************************
* Copyright (c) 2015 - Gray Cat Labs - https://graycat.io
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/

/**
* @file spidriver.h
* @author Alex Hiam - <alex@graycat.io>
*
* @brief A basic driver for controlling Linux spidev interfaces.
*
* Requires an SPI Kernel driver be loaded to expose /dev/spidevX.Y interfaces
* which provide the standard Linux SPI ioctls. This driver is really just an
* ioctl wrapper.
*
* @see
* `examples/spi_ad5689r.c` @include spi_ad5689r.c
*/

#ifndef _SPI_DRIVER_H_
#define _SPI_DRIVER_H_

#include <stdint.h>
#include <linux/spi/spidev.h>

/**
* @brief Opens the /dev/spidev[bus].[cs] interface.
*
* @param bus SPI bus number
* @param cs chip select number
* @return Returns the file descriptor for the spidev interface.
*/
int SPI_open(uint8_t bus, uint8_t cs);

/**
* @brief Closes the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*/
void SPI_close(int spidev_fd);

/**
* @brief Reads from the given spidev interface.
*
* Reads \p n_words from the given spidev interface and puts them into the
* given buffer.
*
* @param spidev_fd spidev file descriptor
* @param rx_buffer pointer to an array, already initialized to the required
* size
* @param n_words the number of words to read into tx_buffer
*
* @return Returns the number of bytes read, or -1 if unable to read from
* interface
*/
int SPI_read(int spidev_fd, void *rx_buffer, int n_words);

/**
* @brief Writes to the given spidev interface.
*
* Writes \p n_words from the given buffer to the given spidev interface.
*
* @param spidev_fd spidev file descriptor
* @param tx_buffer pointer to an array containing the words to be transmitted
* @param n_words the number of words to be transmitted from tx_buffer
*
* @return Returns the number of bytes written, or -1 if unable to write
* interface
*/
int SPI_write(int spidev_fd, void *tx_buffer, int n_words);

/**
* @brief Writes and then reads to the given spidev interface keeping CS unchanged.
*
* Writes \p n_tx_words from \p tx_buffer to the given spidev interface and
* then reads \p n_rx_words from spidev interface and puts them into the
* \p rx_buffer
*
* @param spidev_fd spidev file descriptor
* @param tx_buffer pointer to an array containing the words to be transmitted
* @param n_tx_words the number of words to be transmitted from tx_buffer
* @param rx_buffer pointer to an array, already initialized to the required
* size
* @param n_rx_words the number of words to read into tx_buffer
*
* @return Returns the number of bytes read, or -1 if unable to read or write
* interface
*/
int SPI_transaction(int spidev_fd, void *tx_buffer, int n_tx_words,
void *rx_buffer, int n_rx_words);

/**
* @brief Writes to and reads from the given spidev interface simultaneously.
*
* Writes n_words from the given tx buffer to the given spidev interface, while
* simultaneously reading words into the given rx buffer.
*
* @param spidev_fd spidev file descriptor
* @param tx_buffer pointer to an array containing the words to be transmitted
* @param rx_buffer pointer to an array, already initialized to the required
* size
* @param n_words the number of words to be transferred
*
* @return Returns the number of bytes transferred, or -1 if unable to write
* interface
*/
int SPI_transfer(int spidev_fd, void *tx_buffer, void *rx_buffer, int n_words);

/**
* Passed to #SPI_setBitOrder to specify the bit order to use for subsequent
* SPI transfers.
*/
typedef enum {
SPI_MSBFIRST, ///< Most significant bit first
SPI_LSBFIRST ///< Least significant bit first
} SPI_bit_order;

/**
* @brief Sets the bit order of the given spidev interface.
*
* @param spidev_fd spidev file descriptor
* @param bit_order one of SPI_MSBFIRST or SPI_LSBFIRST
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_setBitOrder(int spidev_fd, SPI_bit_order bit_order);

/**
* @brief Sets the number of bits per word for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
* @param bits_per_word number of bits per word
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_setBitsPerWord(int spidev_fd, uint8_t bits_per_word);

/**
* @brief Gets the number of bits per word for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns bits per word, or -1 if error
*/
int SPI_getBitsPerWord(int spidev_fd);

/**
* @brief Sets the maximum clock frequency for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
* @param frequency maximum clock frequency
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_setMaxFrequency(int spidev_fd, uint32_t frequency);

/**
* @brief Gets the maximum clock frequency for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns the frequency, or -1 if error
*/
int SPI_getMaxFrequency(int spidev_fd);

/**
* @brief Sets the clock mode for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
* @param clock_mode one of SPI_MODE_0, SPI_MODE_1, SPI_MODE_2 or SPI_MODE_3
*
* @return Returns 0 if successful, -1 if error
*/
int SPI_setClockMode(int spidev_fd, uint8_t clock_mode);

/**
* @brief Gets the clock mode for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns the clock mode, or -1 if error
*/
int SPI_getClockMode(int spidev_fd);

/**
* @brief Sets the given spidev interface's cs signal to be active low.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_setCSActiveLow(int spidev_fd);

/**
* @brief Sets the given spidev interface's cs signal to be active high.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_setCSActiveHigh(int spidev_fd);

/**
* @brief Enables the given spidev interface's cs output.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_enableCS(int spidev_fd);

/**
* @brief Disables the given spidev interface's cs output.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_disableCS(int spidev_fd);

/**
* @brief Puts the given spidev interface in loopback mode.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_enableLoopback(int spidev_fd);

/**
* @brief Disables loopback mode for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_disableLoopback(int spidev_fd);

/**
* @brief Enables 3-wire SPI mode for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_enable3Wire(int spidev_fd);

/**
* @brief Enables 4-wire SPI mode for the given spidev interface.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_disable3Wire(int spidev_fd);

/**
* @brief Sets the full SPI mode byte for the given spidev interface.
*
* Used to set things like the clock mode, SC active state, etc., and shouldn't
* typically need to be called directly.
*
* @param spidev_fd spidev file descriptor
* @param mode SPI mode byte
*
* @return Returns 0 if successful, or -1 if error
*/
int SPI_setMode(int spidev_fd, uint8_t mode);

/**
* @brief Gets and returns the full SPI mode byte for the given spidev
* interface.
*
* Encodes current settings like the clock mode, SC active state, etc., and
* shouldn't typically need to be called directly.
*
* @param spidev_fd spidev file descriptor
*
* @return Returns SPI mode if successful, or -1 if error
*/
int SPI_getMode(int spidev_fd);

#endif // _SPI_DRIVER_H_

MAKEFILE

# Makefile for serbus example programs
#
# Run:
# $ make
# to build all the example programs.
#
# The examples can also be built individually, e.g.:
# $ make i2c_htu21d

CC = arm-linux-gcc
CFLAGS = -Wall -O2
INCLUDES = -I../include/
I2C_DRIVER = ../src/i2cdriver.c
SPI_DRIVER = ../src/spidriver.c
BIN_DIR = bin

all: i2c_htu21d spi_ad5689r

.c.o:
$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@

i2cdriver.o: $(I2CDRIVER)
$(CC) $(CFLAGS) $(INCLUDES) -c $(I2C_DRIVER)

spidriver.o: $(I2CDRIVER)
$(CC) $(CFLAGS) $(INCLUDES) -c $(SPI_DRIVER)

i2c_htu21d: i2c_htu21d.o i2cdriver.o
$(CC) -o $(BIN_DIR)/i2c_htu21d $^

spi_ad5689r: spi_ad5689r.o spidriver.o
$(CC) -o $(BIN_DIR)/spi_ad5689r $^

clean:
rm -f *.o bin/*
STEP9:Step 9: Copy the Executable file into the friendlyarm target board using sdcard and execute the program as ./spi_ad5689r


But i did nt get any dac output .Kindly Can any one correct this code.

Re: Tiny6410 Spi Interface

Posted: Thu Aug 31, 2017 8:01 am
by davef
I can't correct the code and quickly reading through it I don't know if you have tried a loop-back test on the Tiny6410 SPI interface or not.

Google spidev_test

Good luck!