uPP Device Driver is dropping data from buffer - c
I have written code for a uPP device driver to be used with an OMAPL138 based custom board for data acquisition through a camera lens. The code for my device driver is:
/*
* A device driver for the Texas Instruments
* Universal Paralllel Port (UPP)
*
* Modified by: Ali Shehryar <github.com/sshehryar>
*
*/
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/delay.h> //for "mdelay(...)"
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/moduleparam.h>
#include <mach/da8xx.h>
#include <asm/sizes.h>
#include <asm/io.h>
#include <mach/mux.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <asm/gpio.h>
//SPRUH77A.PDF, Table 12-1. The Interrupt number assigned to the UPP module.
#define UPP_INTERRUPT 91
//SPRS586D.PDF, Table 2-4
#define DA850_UPP_BASE 0x01E16000
//SPRS586D.PDF, Table 5-117. Offsets from DA850_UPP_BASE
#define UPPCR 0x00000004
#define UPDLB 0x00000008
#define UPCTL 0x00000010
#define UPICR 0x00000014
#define UPIVR 0x00000018
#define UPTCR 0x0000001C
#define UPIER 0x00000024
#define UPIES 0x00000028
#define UPIEC 0x0000002C
#define UPEOI 0x00000030
#define UPID0 0x00000040
#define UPID1 0x00000044
#define UPID2 0x00000048
#define UPIS0 0x00000050
#define UPIS1 0x00000054
#define UPIS2 0x00000058
//SPRS586D.PDF, Table 2-4
#define DA850_PSC1_BASE 0x01E27000
//SPRUH77A.PDF, Table 9-7.
//"Power and Sleep Controller 1" (PSC1) Revision ID Register.
#define PSC_REVID 0x00000000
//SPRUH77A.PDF, Table 9-7.
//"Power Domain Transition Status" Register.
#define PSC_PTSTAT 0x00000128
//SPRUH77A.PDF, Table 9-2, Table 9-7.
//NOTE that in Table 9-2, UPP module has an LPSC number of 19...
#define PSC1_MDCTL_19 0x00000A4C //0xA00 + (19*4).
//SPRUH77A.PDF, Table 9-7.
//"Power Domain Transition Command Register" Register.
#define PSC_PTCMD 0x00000120
//DMA Status Register bitmasks used in the ISR handler....
#define EOLI 16
#define EOWI 8
#define ERRI 4
#define UORI 2
#define DPEI 1
#define UPIES_MASK 0x0000001F
///#define UPIES_MASK 0x1717 ---> Reverted back to 1717 because Window interrupt is not used, for now its useless
//To Enable all interrupts ---> #define UPIES_MASK 0x1F1F
/// Shehryar: These Parameters are to be modified and tweaked according to reqirements in realtime.
//The DMA PARAMETERS
#define UPP_BUF_SIZE 8192 //Need to lookup for an exact val (changed from 8192 on 2 DEc 14)
#define UPP_RX_LINE_COUNT 8 //Changed from 8
#define UPP_RX_LINE_SIZE 1024
#define UPP_RX_LINE_OFFSET 1024 //value changed from 1024 to 0 on 2 DEC 2014
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//MODIFIED BY: SHEHRYAR ---> Pg 888 General-Purpose Input/Output (GPIO) SPRUH82A–December 2011. Added 25-Nov-2014
#define DA850_GPIO_BASE 0x01E26000
//------------------------------------- 9/12/2014-----------------
//#define MY_BUFFER_SIZE 1048756
//----------------------------------------------------------------
//MODIFIED BY: SHEHRYAR. Offsets from GPIO_Base (SPRS653C.PDF TABLE 5-134)
#define DIR67 0x00000088
#define OUT_DATA67 0x0000008C
#define SET_DATA67 0x00000090
#define CLR_DATA67 0x00000094
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void *rxBuf;
static void __iomem *pinmux_base = 0;
static void __iomem *upp_base = 0;
static void __iomem *psc1_base = 0;
static void __iomem *gpio_base = 0;
static DECLARE_WAIT_QUEUE_HEAD(read_queue);
static int32_t read_pending = 0; // changed from static int
//set to '1' when loading this module to use the Digital Loopback (DLB)
//features,e.g:"insmod UPP_driver.ko loopbackMode=1"
static int loopbackMode = 0;
module_param( loopbackMode, int, S_IRUGO);
int EOWI_Count = 0; int UORI_count =0;
///////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////INTERRUPT SERVICE ROUTINE//////////////////////////////////////
//////////////////////////////////////SPRUH82A–December 2014 PAGE 1515////////////////////////////////
//SPRUGJ5B.PDF, Section 2.6.4
static irqreturn_t upp_ISR_handler(int irq, void *dev_id)
{
uint32_t regVal, status;
if (pinmux_base == 0)
{
return IRQ_HANDLED;
}
status = ioread32( upp_base + UPIER );
while (status & 0x0000001F ) //0x1F1F is an interrupt bit-mask
{
//
//DMA Channel I (Channel A), Receiving data (We Need A (DMA Ch I ) to Rx Data instead of Tx ; 27th Nov 2014 - 10:38am)
//
if (status & EOLI)
{
//Clear the interrupt. WRITING ZERO to any other bit has NO effect,
//per SPRUH77A.PDF, section 33.3.9.
iowrite32(EOLI, upp_base + UPIER );
//printk(KERN_INFO "DMA: EOLI\n");
//printk(KERN_INFO "DMA:EOLI. UPP_RX_LINE_SIZE[%d] UPP_RX_LINE_OFFSET[%d] UPP_RX_LINE_COUNT[%d] \n", UPP_RX_LINE_SIZE,UPP_RX_LINE_OFFSET,UPP_RX_LINE_COUNT );
//dump_Channel_regs();
}
if (status & EOWI)
{
//Clear the interrupt. WRITING ZERO to any other bit has NO effect,
//per SPRUH77A.PDF, section 33.3.9.
//printk(KERN_INFO "DMA: EOWI\n");
iowrite32(EOWI, upp_base + UPIER );
read_pending = 8192;
wake_up_interruptible(&read_queue);
//add 1 to EOWI counter
EOWI_Count += 1;
// dump_Channel_regs();
}
if (status & ERRI)
{
//Clear the interrupt. WRITING ZERO to any other bit has NO effect,
//per SPRUH77A.PDF, section 33.3.9.
iowrite32(ERRI, upp_base + UPIER );
//dump_Channel_regs();
}
if (status & UORI)
{
//Clear the interrupt. WRITING ZERO to any other bit has NO effect,
//per SPRUH77A.PDF, section 33.3.9.
iowrite32(UORI, upp_base + UPIER );
UORI_count +=1;
//dump_Channel_regs();
}
if (status & DPEI)
{
//Clear the interrupt. WRITING ZERO to any other bit has NO effect,
//per SPRUH77A.PDF, section 33.3.9.
iowrite32(DPEI, upp_base + UPIER );
//dump_Channel_regs();
}
//read again, and process if necessary.
status = ioread32( upp_base + UPIER );
}
//Clear UPEOI to allow future calls to this function.
regVal = ioread32( upp_base + UPEOI);
regVal &= 0xFFFFFF00;
regVal = 0;// End of Interrupt
iowrite32(regVal, upp_base + UPEOI);
return IRQ_HANDLED;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void pin_mux_set( int index, unsigned int bits )
{
static DEFINE_SPINLOCK(mux_spin_lock);
unsigned long flags;
unsigned int offset;
if ((index < 0) || (index > 19))
{
printk(KERN_INFO "pin_mux_set:index is out of range.\n");
return;
}
if (!pinmux_base)
{
//SRPUH77A.PDF,Table 11-3
if ((pinmux_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K)) == 0)
{
printk(KERN_INFO "pin_mux_set:Cannot fetch pinmux_base.\n");
return;
}
}
offset = 0x120 + (index * 4);
spin_lock_irqsave(&mux_spin_lock, flags);
iowrite32(bits, pinmux_base + offset);
spin_unlock_irqrestore(&mux_spin_lock, flags);
//NOTE: do NOT "iounmap" the pinmux_base pointer, as it is used
// in the ISR_handler.....
}
static void upp_pin_mux_init(void)
{
pin_mux_set( 13, 0x44440000 );
pin_mux_set( 14, 0x44444480 );
pin_mux_set( 15, 0x44444444 );
pin_mux_set( 16, 0x44444444 );
pin_mux_set( 17, 0x44444444 );
pin_mux_set( 18, 0x00444444 );
pin_mux_set( 19, 0x08888800 );
//pin_mux_print() ;
}
//SPRUGJ5B.PDF, Section 2.6.1.2.
static void upp_power_and_clocks( void )
{
/*
* Refer to:
* * Power and Sleep Controller (PSC), Chapter 9 in the TRM(SPRUH77A.PDF)
* * Device Clocking, Chapter 7 (esp Section 7.3.5) in the TRM.
*
*
*/
int regVal;
if (!psc1_base)
{
if ((psc1_base = ioremap(DA850_PSC1_BASE, SZ_4K)) == 0)
{
printk(KERN_INFO "upp_power_and_clocks:Cannot fetch psc1_base.\n");
return;
}
}
regVal = ioread32(psc1_base + PSC_REVID);
//PSC Revision ID should be "44825A00" per SPRUH77A.PDF, section 9.6.1
if (regVal == 0x44825A00)
{
printk( KERN_INFO "PSC_REVID = 0x%08X....OK\n", regVal);
}
else
{
printk( KERN_INFO "********ERROR: PSC_REVID = 0x%08X********\n", regVal);
}
// SPRUH77A.PDF, 9.3.2.1, Table 9-6, 9.6.10
// wait for GOSTAT[0] in PSTAT to clear to 0 ("No transition in progress")
while ( ioread32(psc1_base + PSC_PTSTAT) & 0x00000001 )
;
//
//SPRUH77A.PDF, 9.3.2.2, 9.6.19.
//Set NEXT bit in MDCTL19 to Enable(3h).
regVal = ioread32( psc1_base + PSC1_MDCTL_19 );
regVal |= 0x00000003;
iowrite32(regVal, psc1_base + PSC1_MDCTL_19);
//
//SPRUH77A.PDF, 9.3.2.3, 9.6.9.
//Set the GO[0] bit in PTCMD to 1 to initiate power-domain transition
regVal = ioread32(psc1_base + PSC_PTCMD);
regVal |= 0x00000001;
iowrite32(regVal, psc1_base + PSC_PTCMD);
//
// SPRUH77A.PDF, 9.3.2.4
// Wait for GOSTAT[0] in PTSTAT to clear to 0
while ( ioread32(psc1_base + PSC_PTSTAT) & 0x00000001 )
;
iounmap( psc1_base );
psc1_base = 0;
}
//SPRUGJ5B.PDF, Section 2.6.1.3, 2.6.1.4
static void upp_swrst( void )
{
int32_t reg_val;
if (!upp_base)
{
if ((upp_base = ioremap(DA850_UPP_BASE, SZ_4K)) == 0)
{
printk(KERN_INFO "upp_swrst:Cannot fetch upp_base.\n");
return;
}
}
//Fetch the UPP ID for the sake of sanity....Should be "44231100"
reg_val = ioread32( upp_base + 0 );
if (reg_val == 0x44231100 )
{
printk(KERN_INFO "UPP_UPPID = 0x%08X....OK\n", reg_val);
}
else
{
printk( KERN_INFO "********ERROR: UPP_UPPID = 0x%08X********\n", reg_val);
}
// SPRUH77A.PDF, Section 33.2.7.1.1, Table 33-12.
// clear EN bit of UPPCR to (temporarily) disable the UPP.
reg_val = ioread32( upp_base + UPPCR );
reg_val &= ~(1 << 3); //0xfffffff7;
iowrite32( reg_val, upp_base + UPPCR );
// SPRUH77A.PDF, Section 33.2.7.1.2, Table 33-12.
//poll "DMA Burst" (DB) bit of UPPCR to ensure DMA controller is idle
while ( ioread32( upp_base + UPPCR ) & (1 << 7) )
;
// SPRUH77A.PDF, Section 33.2.7.1.3, Table 33-12.
// assert SWRST bit (bit 4) of UPPCR
reg_val = ioread32( upp_base + UPPCR );
reg_val |= 0x00000010;
iowrite32( reg_val, upp_base + UPPCR );
//
// wait at least 200 clock cycles
// (SPRUGJ5B.PDF, 2.6.1.4)
mdelay( 200 ); // abitrary choice of 200ms
// SPRUH77A.PDF, Section 33.2.7.1.4 --AND--
// SPRUGJ5B.PDF, 2.6.1.4
// clear SWRST bit (bit 4) of UPPCR
reg_val = ioread32( upp_base + UPPCR );
reg_val &= 0xffffffef;
iowrite32( reg_val, upp_base + UPPCR );
}
//SPRUGJ5B.PDF, Section 2.6.1.5
static void upp_config( void )
{
int32_t regVal;
//-------------------------------------------------------------------------
// UPPCTL - UPP Interface Channel Settings....SPRUH77A.PDF, Section 33.3.4.
//
// - DATA and XDATA Pin assignments to Channels A & B:
// Refer to SPRUGJ5B.PDF, Table 3:
//
// ____PHYSICAL_PINS___|____CHANNEL_ASSIGNMENT___
// * DATA[7:0] | A[7:0]
// * DATA[15:8] | B[7:0]
//-------------------------------------------------------------------------
regVal = 0;
regVal |= 1 << 17; // IWA - CHANNEL A 8/16bit MODE: Set Channel A to 16 bit mode
iowrite32( regVal, upp_base + UPCTL );
//-------------------------------------------------------------------------
// UPPICR - signal enable, signal inversion, clk div (tx only), etc.
// SPRUH77A.PDF, Section 33.3.5
//-------------------------------------------------------------------------
regVal = 0; //Channel A: START is active-high
regVal |= 1<<3; //Channel A:STARTA is honored in Rev Mode
regVal |= 1<<4; //Channel A:ENABLEA is honored in Rev Mode
regVal |= 1<<12; //Channel A:(CLKINVA) Signal on rising edge of clock
regVal |= 1<<13; //Channel A:(TRISA) pins are High-impedence while idle
iowrite32( regVal, upp_base + UPICR );
//-------------------------------------------------------------------------
// UPPIVR - Idle Value Register
// SPRUH77A.PDF, Section 33.3.5
//-------------------------------------------------------------------------
regVal = 0;
regVal |= 0xab00; //Channel B Idle Value
regVal |= 0x00cd; //Channel A Idle Value
iowrite32( regVal, upp_base + UPIVR );
//-------------------------------------------------------------------------
// UPTCR - i/o tx thresh (tx only), dma read burst size
//-------------------------------------------------------------------------
regVal = 0x00000003; //DMA Channel I READ-threshold. 256 bytes (max)
iowrite32(regVal, upp_base + UPTCR );
}
//SPRUGJ5B.PDF, Section 2.6.1.6
static void upp_interrupt_enable( void )
{
int32_t regVal, status;
// Register the ISR before enabling the interrupts....
status = request_irq( UPP_INTERRUPT, upp_ISR_handler, 0, "upp_ISR", 0 );
if( status < 0 )
{
return;
}
// clear all interrupts
iowrite32( UPIES_MASK, upp_base + UPIEC );
//------------------------------------------------------------------------
//Dumping Registers again for debugging purpose
//dump_Channel_regs();
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// UPIES - Interrupt Enable. Interrupt events will generate a CPU interrupt
//-------------------------------------------------------------------------
// regVal = 0x17; //Enable ALL interrupts (but EOWI) for DMA Channel I
//regVal |= 0x17 << 8; //Enable ALL interrupts (but EOWQ) for DMA Channel Q
regVal = UPIES_MASK;
iowrite32( regVal, upp_base + UPIES );
}
//SPRUGJ5B.PDF, Section 2.6.1.7
static void upp_enable( void )
{
int32_t reg_val;
// set EN bit in UPPCR.
// The EN bit (effectively disabling the UPP peripheral)...
// was cleared in "upp_swrst()" function
reg_val = ioread32( upp_base + UPPCR );
reg_val |= 1 << 3;
iowrite32( reg_val, upp_base + UPPCR );
}
static void upp_disable( void )
{
int32_t reg_val;
reg_val = ioread32( upp_base + UPPCR );
reg_val &= ~(1 << 3); //0xfffffff7;
iowrite32( reg_val, upp_base + UPPCR );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////MODIFIED BY: SHEHRYAR ; 25-NOV-2014 4:40pm PST (+5.00 GMT)///////////////////
static void setpin_GPIO (void)
{
int32_t reg_val=0;
if ((gpio_base = ioremap(DA850_GPIO_BASE, SZ_4K)) == 0)
{
return;
}
//reg_val = ioread32(gpio_base + SET_DATA67);
reg_val |= (1<<6); ///Set Pin 6 of Bank 6 GP6P6 to 1 to drive GPIO high
iowrite32(reg_val,gpio_base + SET_DATA67);
}
static void clrpin_GPIO(void)
{
int32_t reg_val=0;
if ((gpio_base = ioremap(DA850_GPIO_BASE, SZ_4K)) == 0)
{
return;
}
//reg_val = ioread32(gpio_base + CLR_DATA67);
/*reg_val |= ~(1<<0);
reg_val |= ~(1<<1);
reg_val |= ~(1<<2);
reg_val |= ~(1<<3);
reg_val |= ~(1<<4);*/
reg_val |= (1<<6); //Set Pin 6 of bank 6 GP6P6 of CLR_DATA67 Register to High to drive GPIO signals low
iowrite32(reg_val,gpio_base + CLR_DATA67);
}
///////Function to set DIR to 1 for GP6P5//////////////////////////////////////////////////////////////
static void Config_GPIO(void)
{
int32_t reg_val;
if ((gpio_base = ioremap(DA850_GPIO_BASE, SZ_4K)) == 0)
{
return;
}
//set dir
reg_val = ioread32(gpio_base + DIR67);
reg_val &= ~(1<<0);
reg_val &= ~(1<<1);
reg_val &= ~(1<<2);
reg_val &= ~(1<<3);
reg_val &= ~(1<<4);
reg_val &= ~(1<<6);
iowrite32(reg_val,gpio_base + DIR67);
printk(KERN_INFO "DIR67 => [0x%08X]\n", reg_val);
//set to high
reg_val = ioread32(gpio_base + SET_DATA67);
reg_val |= (1<<0);
reg_val |= (1<<1);
reg_val |= (1<<2);
reg_val |= (1<<3);
reg_val |= (1<<4);
iowrite32(reg_val,gpio_base + SET_DATA67);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//SPRUGJ5B.PDF, Section 2.6.1.8
// Return false on error
static bool upp_mem_alloc( void )
{
//rxBuf2 = kcalloc( 1 , UPP_BUF_SIZE, GFP_KERNEL | GFP_DMA );
rxBuf = kcalloc( 1 , UPP_BUF_SIZE, GFP_KERNEL | GFP_DMA );
if (!rxBuf) //|| (!rxBuf2)
{
return false;
}
return true;
}
static void upp_program_DMA_channelA( void )
{
while ( ioread32( upp_base + UPIS2 ) & 0x00000002 );
//------------------------------------------------------------------------
// Channel A (Rx), (DMA Channel I) //27th Nov 2014 - 11:08 am
//------------------------------------------------------------------------
iowrite32( rxBuf, upp_base + UPID0);
iowrite32( ( (UPP_RX_LINE_COUNT << 16) | UPP_RX_LINE_SIZE ), upp_base + UPID1);
iowrite32( UPP_RX_LINE_OFFSET, upp_base + UPID2);
}
//------------------------------------------------------------------------
// User-mode functions read/write/open/close, etc.
//------------------------------------------------------------------------
int upp_open( struct inode *iPtr, struct file *fPtr )
{
int minor,major;
read_pending = 0;
minor=iminor(iPtr);
major=imajor(iPtr);
printk( KERN_INFO "upp_open: MAJOR(%d), MINOR(%d)\n", major, minor);
upp_disable();
upp_enable();
return 0;
}
////////////////////////////////////////////////////////////READ FUNCTION STARTS HERE!!!!!!////
/*-------------------------------------------------------------------------------------------*/
//int count_missing_data=0;
//static int read_flag =0;
ssize_t upp_read( struct file *fPtr, char __user *buffer, size_t size, loff_t *offset )
{
int readBytes = 0;
int retVal=0;
void *bPtr = (void *)buffer;
if (!bPtr) {return -1; printk(KERN_INFO "ERROR: bPtr not initilized\n");}
printk(KERN_INFO "Reading %d Bytes ...\n",size );
while (readBytes<size)
{
//read_flag+=1;
read_pending = 0;
//mdelay(10);
memset(rxBuf,255,8192);
//memset(rxBuf2,128,8192);
upp_program_DMA_channelA();
clrpin_GPIO();
wait_event_interruptible( read_queue, read_pending > 0 );
while ( ioread32( upp_base + UPIS2 ) & 0x00000001 )
{
printk (KERN_INFO "DMA IS STILL ACTIVE! \n");
}
setpin_GPIO(); // Set High
retVal = copy_to_user(bPtr,rxBuf,read_pending);
if(retVal)
{
printk(KERN_INFO "ERROR: Copy to user failed!\n");
return readBytes;
}
readBytes += read_pending;
bPtr += 8192;
} //end of while loop
printk(KERN_INFO"\nRead [%d] Bytes.\n",readBytes);
printk(KERN_INFO"END OF WINDOW (EOWI) INTERRUPT Count = [%d]\n", EOWI_Count);
printk(KERN_INFO"DMA UNDERRUN OR OVERFLOW (UORI) Interrupt Count = %d\n", UORI_count);
return readBytes;
//read_flag += 1;
//mdelay(9);
}
//////////////////////////////////////////////READ() FUNCTION ENDS HERE!!!!!!///////////////////////////
/*---------------------------------------------------------------------------------------------------*/
int upp_release( struct inode *iPtr, struct file *fPtr )
{
return 0;
printk(KERN_INFO "upp_release completed.\n");
}
static struct cdev *UPP_cdev;
static dev_t UPP_MajorMinorNumbers;
struct file_operations upp_fops = {
.owner = THIS_MODULE,
//.llseek = no_llseek,
//.poll = upp_poll,
.read = upp_read,
//.write = upp_write,
//.ioctl = upp_ioctl,
.open = upp_open,
//.release = upp_release,
};
/*
* Return ZERO on success.
*
*/
static int __init upp_init(void)
{
int retVal;
//printk(KERN_INFO "Entering upp_init().\n");
//SPRUGJ5B.PDF, Section 2.6.1.8
// I'm doing this out-of-order...If the mem-allocation fails,
// there is no sense in doing anything else, except to bail early...
if (upp_mem_alloc() == false)
{
printk(KERN_INFO "******ERROR: Could not allocate buffers. Bailing!******\n");
return -1;
}
//--------------------------------------------------------
//--------------------------------------------------------
//SPRUGJ5B.PDF, Section 2.6.1.1
upp_pin_mux_init();
printk(KERN_INFO "upp_pin_mux_init()...OK.\n");
//SPRUGJ5B.PDF, Section 2.6.1.2.
upp_power_and_clocks();
printk(KERN_INFO "upp_power_and_clocks()...OK.\n");
//SPRUGJ5B.PDF, Section 2.6.1.3, 2.6.1.4
upp_swrst();
printk(KERN_INFO "upp_swrst()...OK.\n");
//SPRUGJ5B.PDF, Section 2.6.1.5
upp_config();
printk(KERN_INFO "upp_config()...OK.\n");
//SPRUGJ5B.PDF, Section 2.6.1.6
upp_interrupt_enable();
printk(KERN_INFO "upp_interrupt_enable()...OK.\n");
//SPRUGJ5B.PDF, Section 2.6.1.7
upp_enable();
printk(KERN_INFO "upp_enable()...OK.\n");
//---------------------------SETTING GPIOS----------------
Config_GPIO();
//--------------------------------------------------------
setpin_GPIO(); // Set High
UPP_MajorMinorNumbers = MKDEV( 0, 0);
if ( (retVal = alloc_chrdev_region( &UPP_MajorMinorNumbers, 0, 1, "UPP" )) < 0)
{
printk(KERN_INFO "ERROR: Major/Minor number allocation failed.\n");
return retVal;
}
UPP_cdev = cdev_alloc();
UPP_cdev->ops = &upp_fops;
UPP_cdev->owner = THIS_MODULE;
if (cdev_add( UPP_cdev, UPP_MajorMinorNumbers, 1) != 0)
{
printk(KERN_INFO "ERROR: UPP driver NOT loaded. CDEV registration failed.\n");
}
else
{
printk(KERN_INFO "\nUPP Major: %d , Minor: %d \n", MAJOR(UPP_MajorMinorNumbers), MINOR(UPP_MajorMinorNumbers));
}
printk("UPP driver (1.8.0 - 5/January/2015) succesfully installed.\n");
return 0;
}
/*
*
*
*
*/
static void __exit upp_exit(void)
{
uint32_t regVal;
printk(KERN_INFO "Exiting..Initializing upp_exit call......\n");
// SPRUH77A.PDF, Section 33.2.7.1.1, Table 33-12.
// clear EN bit of UPPCR to disable the UPP.
regVal = ioread32( upp_base + UPPCR );
regVal &= 0xfffffff7;
iowrite32( regVal, upp_base + UPPCR );
free_irq( UPP_INTERRUPT, 0);
if (rxBuf)
{
kfree( rxBuf );
rxBuf = 0;
}
/*if (rxBuf2)
{
kfree(rxBuf2);
rxBuf2=0;
}*/
cdev_del( UPP_cdev );
unregister_chrdev_region( UPP_MajorMinorNumbers, 1);
clrpin_GPIO(); //added 2-Dec-2014
printk(KERN_INFO "UPP driver unloaded (Successful Exit). \n");
}
MODULE_AUTHOR("Ali Shehryar & Umair Ali");
MODULE_DESCRIPTION("OMAP-L138/AM-1808 UPP bus driver");
MODULE_LICENSE("GPL");
module_init(upp_init)
module_exit(upp_exit)
With the code, I have made a tester application that writes a .264 based output file consisting of data acquired from my custom board which I call upp_tester.cpp :
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv )
{
int count = 0;
const int BytesToRead = 8192*4; //32kB
int bytesRead = -1;
int bytesWritten = -1 ;
int upp_fd,out_fd;
char readBuff[BytesToRead] = {0,};
int k = 0;
readBuff[BytesToRead]={'\0'};
upp_fd = open( "/dev/upp", O_RDWR);
out_fd = open( "output_upp.264", O_RDWR|O_CREAT);
for(count = 0; count<100; count++)
{
if (upp_fd == -1)
{
fprintf( stderr , "OPEN failed [%d] - UPP\n" , errno );
if (errno == 2)
{
fprintf( stderr , "NOTE! Check that /dev/upp actually exists, and has the right permissions \n");
}
return errno;
}
if (out_fd == -1)
{
fprintf( stderr , "OPEN /output_upp failed [%d] - Out.dat\n" , errno );
}
//fprintf( stderr , "UPP TESTER Version 1.0.17\n\n" );
for (k=0;k<32;k++)
{
bytesRead = read( upp_fd, readBuff, sizeof (readBuff));
fprintf( stderr , "READ [%d] bytes out of [%d] bytes\n" , bytesRead , sizeof(readBuff) );
bytesWritten = write( out_fd, readBuff, bytesRead );
fprintf( stderr , "WROTE [%d] bytes out of [%d] bytes\n" , bytesWritten , sizeof(readBuff) );
}
}
close(upp_fd);
close(out_fd);
return 0;
}
Now when I explore the output file, I see that a lot of bytes are missing which I usually set to 255 using memset. This is because the core I have programmed for my dsp processor for the tester file is set to throw fixed data from 0-99. What could be causing this problem. I am a total newbie to device drivers and kernel level programming so any help would be deeply appreciated.
In case you would like to submit the driver to upstream (which actually a right way to go), you probably may contact with TI guys who are working with DaVinci boards. I assume you already googled the link DaVinci Wiki.
Regarding to your driver it's hard to check due to its code style. Moreover you may check modern kernel APIs for stuff you are trying to program manually (voltage regulators, clocks, pin control, and so on). For many basic devices there are already drivers and setup. Also you have to describe you board configuration in device tree. Usually it means to create an additional (to the common) piece of configuration and put it under arch/arm/boot/dts/. There you may find da850 predefined data bases.
Related
How can I use esp-idf-mirf nRF24L01/Si24R1 driver to transmit audio sampled with i2s_read?
Original post - 12/2022 I'm currently trying to transmit audio via nRF24 with an ESP32 development board using a driver library from nopnop2002. I'm using the i2s-adc lib functions to get data from an analog microphone and save it into a buffer after scaling it down to 8-bit so that the other esp32 can play it directly with its DAC converter. At first, I had a large i2s_read_len and later I suspected that this could be an issue so I decided to reduce it to 12-bit (the ADC bit width of the esp32) for testing purposes. What is the ideal size for my audio packets? I'm also not sure how to handle the sampling rate of i2s_read and the rate at which I attempt to send data to the nRF24 via SPI. As far as I know the internal ADC-I2S already has a queue implemented, should I make another one and use vTaskDelay(??? / portTICK_PERIOD_MS); to avoid sending data too fast? Here's what I currently have ignoring the includes: #define V_REF 1100 #define I2S_COMM_MODE 0 // ADC/DAC Mode #define I2S_SAMPLE_RATE 44100 #define I2S_SAMPLE_BITS 16 #define I2S_BUF_DEBUG 0 // enable display buffer for debug #define I2S_READ_LEN 16 * 1024 // I2S read buffer length #define I2S_FORMAT (I2S_CHANNEL_FMT_ONLY_RIGHT) #define I2S_CHANNEL_NUM 0 // I2S channel number #define I2S_ADC_UNIT ADC_UNIT_1 // I2S built-in ADC unit #define I2S_ADC_CHANNEL ADC1_CHANNEL_0 // I2S built-in ADC channel GPIO36 #define BIT_SAMPLE 16 #define SPI_DMA_CHAN SPI_DMA_CH_AUTO #define NUM_CHANNELS 1 // For mono recording only! #define SAMPLE_SIZE (BIT_SAMPLE * 1024) #define BYTE_RATE (I2S_SAMPLE_RATE * (BIT_SAMPLE / 8)) * NUM_CHANNELS /** * #brief I2S ADC mode init. */ void init_microphone(void) { int i2s_num = I2S_COMM_MODE; i2s_config_t i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN | I2S_MODE_ADC_BUILT_IN, .sample_rate = I2S_SAMPLE_RATE, .bits_per_sample = I2S_SAMPLE_BITS, .communication_format = I2S_COMM_FORMAT_STAND_MSB, .channel_format = I2S_FORMAT, .intr_alloc_flags = 0, .dma_buf_count = 6, .dma_buf_len = 256, .use_apll = 1, }; // Call driver installation function and adc pad. ESP_ERROR_CHECK(i2s_driver_install(i2s_num, &i2s_config, 0, NULL)); ESP_ERROR_CHECK(i2s_set_adc_mode(I2S_ADC_UNIT, I2S_ADC_CHANNEL)); } /** * #brief Scale data to 8bit for data from ADC. * Data from ADC are 12bit width by default. * #param d_buff: destination buffer * #param s_buff: source buffer * #param len: length of source buffer */ void i2s_adc_data_scale(uint8_t *d_buff, uint8_t *s_buff, uint32_t len) { uint32_t j = 0; uint32_t dac_value = 0; #if (EXAMPLE_I2S_SAMPLE_BITS == 16) for (int i = 0; i < len; i += 2) { dac_value = ((((uint16_t)(s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0])))); d_buff[j++] = 0; d_buff[j++] = dac_value * 256 / 4096; } #else for (int i = 0; i < len; i += 4) { dac_value = ((((uint16_t)(s_buff[i + 3] & 0xf) << 8) | ((s_buff[i + 2])))); d_buff[j++] = 0; d_buff[j++] = 0; d_buff[j++] = 0; d_buff[j++] = dac_value * 256 / 4096; } #endif } #if CONFIG_TRANSMITTER void transmitter(void *pvParameters) { size_t bytes_read; ESP_LOGI(pcTaskGetName(0), "Start"); int i2s_read_len = (12); char *i2s_read_buff = (char *)calloc(i2s_read_len, sizeof(char)); uint8_t *i2s_write_buff = (uint8_t *)calloc(i2s_read_len, sizeof(char)); i2s_adc_enable(I2S_CHANNEL_NUM); NRF24_t dev; Nrf24_init(&dev); uint8_t payload = sizeof(i2s_read_buff); uint8_t channel = 90; Nrf24_config(&dev, channel, payload); // Set the receiver address using 5 characters esp_err_t ret = Nrf24_setTADDR(&dev, (uint8_t *)"FGHIJ"); if (ret != ESP_OK) { ESP_LOGE(pcTaskGetName(0), "nrf24l01 not installed"); while (1) { vTaskDelay(1); } } #if CONFIG_ADVANCED AdvancedSettings(&dev); #endif // CONFIG_ADVANCED // Print settings Nrf24_printDetails(&dev); // Start ADC while (1) { // Read data from I2S bus, in this case, from ADC. // i2s_read(I2S_CHANNEL_NUM, (void *)i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY); // process data and scale to 8bit for I2S DAC. i2s_adc_data_scale(i2s_write_buff, (uint8_t *)i2s_read_buff, i2s_read_len); // i2s_write_buff needs to be the buffer that is sent via nr24l01. Nrf24_send(&dev, i2s_write_buff); if (Nrf24_isSend(&dev, 1000)) { ESP_LOGI(pcTaskGetName(0), "sending audio data ..."); } else { ESP_LOGW(pcTaskGetName(0), "sending failed ..."); } } } #endif // CONFIG_TRANSMITTER void app_main(void) { // I2S ADC mode microphone init. init_microphone(); #if CONFIG_TRANSMITTER xTaskCreate(transmitter, "TRANSMITTER", 1024 * 3, NULL, 2, NULL); #endif // Stop I2S driver and destroy // ESP_ERROR_CHECK(i2s_driver_uninstall(I2S_COMM_MODE)); } Here is the code where Nrf24_send is defined: #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include <driver/spi_master.h> #include <driver/gpio.h> #include "esp_log.h" #include "mirf.h" #define TAG "NRF24" // SPI Stuff #if CONFIG_SPI2_HOST #define HOST_ID SPI2_HOST #elif CONFIG_SPI3_HOST #define HOST_ID SPI3_HOST #endif static const int SPI_Frequency = 4000000; // Stable even with a long jumper cable //static const int SPI_Frequency = 6000000; //static const int SPI_Frequency = 8000000; // Requires a short jumper cable //static const int SPI_Frequency = 10000000; // Unstable even with a short jumper cable const char rf24_datarates[][8] = {"1Mbps", "2Mbps", "250Kbps"}; const char rf24_crclength[][10] = {"Disabled", "8 bits", "16 bits"}; const char rf24_pa_dbm[][8] = {"PA_MIN", "PA_LOW", "PA_HIGH", "PA_MAX"}; void Nrf24_init(NRF24_t * dev) { esp_err_t ret; ESP_LOGI(TAG, "CONFIG_MISO_GPIO=%d", CONFIG_MISO_GPIO); ESP_LOGI(TAG, "CONFIG_MOSI_GPIO=%d", CONFIG_MOSI_GPIO); ESP_LOGI(TAG, "CONFIG_SCLK_GPIO=%d", CONFIG_SCLK_GPIO); ESP_LOGI(TAG, "CONFIG_CE_GPIO=%d", CONFIG_CE_GPIO); ESP_LOGI(TAG, "CONFIG_CSN_GPIO=%d", CONFIG_CSN_GPIO); //gpio_pad_select_gpio(CONFIG_CE_GPIO); gpio_reset_pin(CONFIG_CE_GPIO); gpio_set_direction(CONFIG_CE_GPIO, GPIO_MODE_OUTPUT); gpio_set_level(CONFIG_CE_GPIO, 0); //gpio_pad_select_gpio(CONFIG_CSN_GPIO); gpio_reset_pin(CONFIG_CSN_GPIO); gpio_set_direction(CONFIG_CSN_GPIO, GPIO_MODE_OUTPUT); gpio_set_level(CONFIG_CSN_GPIO, 1); spi_bus_config_t spi_bus_config = { .sclk_io_num = CONFIG_SCLK_GPIO, .mosi_io_num = CONFIG_MOSI_GPIO, .miso_io_num = CONFIG_MISO_GPIO, .quadwp_io_num = -1, .quadhd_io_num = -1 }; ret = spi_bus_initialize( HOST_ID, &spi_bus_config, SPI_DMA_CH_AUTO ); ESP_LOGI(TAG, "spi_bus_initialize=%d",ret); assert(ret==ESP_OK); spi_device_interface_config_t devcfg; memset( &devcfg, 0, sizeof( spi_device_interface_config_t ) ); devcfg.clock_speed_hz = SPI_Frequency; // It does not work with hardware CS control. //devcfg.spics_io_num = csn_pin; // It does work with software CS control. devcfg.spics_io_num = -1; devcfg.queue_size = 7; devcfg.mode = 0; devcfg.flags = SPI_DEVICE_NO_DUMMY; spi_device_handle_t handle; ret = spi_bus_add_device( HOST_ID, &devcfg, &handle); ESP_LOGI(TAG, "spi_bus_add_device=%d",ret); assert(ret==ESP_OK); dev->cePin = CONFIG_CE_GPIO; dev->csnPin = CONFIG_CSN_GPIO; dev->channel = 1; dev->payload = 16; dev->_SPIHandle = handle; } bool spi_write_byte(NRF24_t * dev, uint8_t* Dataout, size_t DataLength ) { spi_transaction_t SPITransaction; if ( DataLength > 0 ) { memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); SPITransaction.length = DataLength * 8; SPITransaction.tx_buffer = Dataout; SPITransaction.rx_buffer = NULL; spi_device_transmit( dev->_SPIHandle, &SPITransaction ); } return true; } bool spi_read_byte(NRF24_t * dev, uint8_t* Datain, uint8_t* Dataout, size_t DataLength ) { spi_transaction_t SPITransaction; if ( DataLength > 0 ) { memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); SPITransaction.length = DataLength * 8; SPITransaction.tx_buffer = Dataout; SPITransaction.rx_buffer = Datain; spi_device_transmit( dev->_SPIHandle, &SPITransaction ); } return true; } uint8_t spi_transfer(NRF24_t * dev, uint8_t address) { uint8_t datain[1]; uint8_t dataout[1]; dataout[0] = address; //spi_write_byte(dev, dataout, 1 ); spi_read_byte(dev, datain, dataout, 1 ); return datain[0]; } void spi_csnHi(NRF24_t * dev) { gpio_set_level( dev->csnPin, 1 ); } void spi_csnLow(NRF24_t * dev) { gpio_set_level( dev->csnPin, 0 ); } // Sets the important registers in the MiRF module and powers the module // in receiving mode // NB: channel and payload must be set now. void Nrf24_config(NRF24_t * dev, uint8_t channel, uint8_t payload) { dev->channel = channel; dev->payload = payload; Nrf24_configRegister(dev, RF_CH, dev->channel); // Set RF channel Nrf24_configRegister(dev, RX_PW_P0, dev->payload); // Set length of incoming payload Nrf24_configRegister(dev, RX_PW_P1, dev->payload); Nrf24_powerUpRx(dev); // Start receiver Nrf24_flushRx(dev); } // Sets the receiving device address //void Nrf24_setRADDR(NRF24_t * dev, uint8_t * adr) esp_err_t Nrf24_setRADDR(NRF24_t * dev, uint8_t * adr) { esp_err_t ret = ESP_OK; Nrf24_writeRegister(dev, RX_ADDR_P1, adr, mirf_ADDR_LEN); uint8_t buffer[5]; Nrf24_readRegister(dev, RX_ADDR_P1, buffer, sizeof(buffer)); for (int i=0;i<5;i++) { ESP_LOGD(TAG, "adr[%d]=0x%x buffer[%d]=0x%x", i, adr[i], i, buffer[i]); if (adr[i] != buffer[i]) ret = ESP_FAIL; } return ret; } // Sets the transmitting device address //void Nrf24_setTADDR(NRF24_t * dev, uint8_t * adr) esp_err_t Nrf24_setTADDR(NRF24_t * dev, uint8_t * adr) { esp_err_t ret = ESP_OK; Nrf24_writeRegister(dev, RX_ADDR_P0, adr, mirf_ADDR_LEN); //RX_ADDR_P0 must be set to the sending addr for auto ack to work. Nrf24_writeRegister(dev, TX_ADDR, adr, mirf_ADDR_LEN); uint8_t buffer[5]; Nrf24_readRegister(dev, RX_ADDR_P0, buffer, sizeof(buffer)); for (int i=0;i<5;i++) { ESP_LOGD(TAG, "adr[%d]=0x%x buffer[%d]=0x%x", i, adr[i], i, buffer[i]); if (adr[i] != buffer[i]) ret = ESP_FAIL; } return ret; } // Add the receiving device address void Nrf24_addRADDR(NRF24_t * dev, uint8_t pipe, uint8_t adr) { uint8_t value; Nrf24_readRegister(dev, EN_RXADDR, &value, 1); if (pipe == 2) { Nrf24_configRegister(dev, RX_PW_P2, dev->payload); Nrf24_configRegister(dev, RX_ADDR_P2, adr); value = value | 0x04; Nrf24_configRegister(dev, EN_RXADDR, value); } else if (pipe == 3) { Nrf24_configRegister(dev, RX_PW_P3, dev->payload); Nrf24_configRegister(dev, RX_ADDR_P3, adr); value = value | 0x08; Nrf24_configRegister(dev, EN_RXADDR, value); } else if (pipe == 4) { Nrf24_configRegister(dev, RX_PW_P4, dev->payload); Nrf24_configRegister(dev, RX_ADDR_P4, adr); value = value | 0x10; Nrf24_configRegister(dev, EN_RXADDR, value); } else if (pipe == 5) { Nrf24_configRegister(dev, RX_PW_P5, dev->payload); Nrf24_configRegister(dev, RX_ADDR_P5, adr); value = value | 0x20; Nrf24_configRegister(dev, EN_RXADDR, value); } } // Checks if data is available for reading extern bool Nrf24_dataReady(NRF24_t * dev) { // See note in getData() function - just checking RX_DR isn't good enough uint8_t status = Nrf24_getStatus(dev); if ( status & (1 << RX_DR) ) return 1; // We can short circuit on RX_DR, but if it's not set, we still need // to check the FIFO for any pending packets //return !Nrf24_rxFifoEmpty(dev); return 0; } // Get pipe number for reading uint8_t Nrf24_getDataPipe(NRF24_t * dev) { uint8_t status = Nrf24_getStatus(dev); return ((status & 0x0E) >> 1); } extern bool Nrf24_rxFifoEmpty(NRF24_t * dev) { uint8_t fifoStatus; Nrf24_readRegister(dev, FIFO_STATUS, &fifoStatus, sizeof(fifoStatus)); return (fifoStatus & (1 << RX_EMPTY)); } // Reads payload bytes into data array extern void Nrf24_getData(NRF24_t * dev, uint8_t * data) { spi_csnLow(dev); // Pull down chip select spi_transfer(dev, R_RX_PAYLOAD ); // Send cmd to read rx payload spi_read_byte(dev, data, data, dev->payload); // Read payload spi_csnHi(dev); // Pull up chip select // NVI: per product spec, p 67, note c: // "The RX_DR IRQ is asserted by a new packet arrival event. The procedure // for handling this interrupt should be: 1) read payload through SPI, // 2) clear RX_DR IRQ, 3) read FIFO_STATUS to check if there are more // payloads available in RX FIFO, 4) if there are more data in RX FIFO, // repeat from step 1)." // So if we're going to clear RX_DR here, we need to check the RX FIFO // in the dataReady() function Nrf24_configRegister(dev, STATUS, (1 << RX_DR)); // Reset status register } // Clocks only one byte into the given MiRF register void Nrf24_configRegister(NRF24_t * dev, uint8_t reg, uint8_t value) { spi_csnLow(dev); spi_transfer(dev, W_REGISTER | (REGISTER_MASK & reg)); spi_transfer(dev, value); spi_csnHi(dev); } // Reads an array of bytes from the given start position in the MiRF registers void Nrf24_readRegister(NRF24_t * dev, uint8_t reg, uint8_t * value, uint8_t len) { spi_csnLow(dev); spi_transfer(dev, R_REGISTER | (REGISTER_MASK & reg)); spi_read_byte(dev, value, value, len); spi_csnHi(dev); } // Writes an array of bytes into inte the MiRF registers void Nrf24_writeRegister(NRF24_t * dev, uint8_t reg, uint8_t * value, uint8_t len) { spi_csnLow(dev); spi_transfer(dev, W_REGISTER | (REGISTER_MASK & reg)); spi_write_byte(dev, value, len); spi_csnHi(dev); } // Sends a data package to the default address. Be sure to send the correct // amount of bytes as configured as payload on the receiver. void Nrf24_send(NRF24_t * dev, uint8_t * value) { uint8_t status; status = Nrf24_getStatus(dev); while (dev->PTX) // Wait until last paket is send { status = Nrf24_getStatus(dev); if ((status & ((1 << TX_DS) | (1 << MAX_RT)))) { dev->PTX = 0; break; } } Nrf24_ceLow(dev); Nrf24_powerUpTx(dev); // Set to transmitter mode , Power up spi_csnLow(dev); // Pull down chip select spi_transfer(dev, FLUSH_TX ); // Write cmd to flush tx fifo spi_csnHi(dev); // Pull up chip select spi_csnLow(dev); // Pull down chip select spi_transfer(dev, W_TX_PAYLOAD ); // Write cmd to write payload spi_write_byte(dev, value, dev->payload); // Write payload spi_csnHi(dev); // Pull up chip select Nrf24_ceHi(dev); // Start transmission } // Test if chip is still sending. // When sending has finished return chip to listening. bool Nrf24_isSending(NRF24_t * dev) { uint8_t status; if (dev->PTX) { status = Nrf24_getStatus(dev); if ((status & ((1 << TX_DS) | (1 << MAX_RT)))) {// if sending successful (TX_DS) or max retries exceded (MAX_RT). Nrf24_powerUpRx(dev); return false; } return true; } return false; } // Test if Sending has finished or retry is over. // When sending has finished return trur. // When reach maximum number of TX retries return false. bool Nrf24_isSend(NRF24_t * dev, int timeout) { uint8_t status; TickType_t startTick = xTaskGetTickCount(); if (dev->PTX) { while(1) { status = Nrf24_getStatus(dev); /* if sending successful (TX_DS) or max retries exceded (MAX_RT). */ if (status & (1 << TX_DS)) { // Data Sent TX FIFO interrup Nrf24_powerUpRx(dev); return true; } if (status & (1 << MAX_RT)) { // Maximum number of TX retries interrupt ESP_LOGW(TAG, "Maximum number of TX retries interrupt"); Nrf24_powerUpRx(dev); return false; } // I believe either TX_DS or MAX_RT will always be notified. // Therefore, it is unusual for neither to be notified for a period of time. // I don't know exactly how to respond. TickType_t diffTick = xTaskGetTickCount() - startTick; if ( (diffTick * portTICK_PERIOD_MS) > timeout) { ESP_LOGE(TAG, "Status register timeout. status=0x%x", status); return false; } vTaskDelay(1); } } return false; } uint8_t Nrf24_getStatus(NRF24_t * dev) { uint8_t rv; Nrf24_readRegister(dev, STATUS, &rv, 1); return rv; } void Nrf24_powerUpRx(NRF24_t * dev) { dev->PTX = 0; Nrf24_ceLow(dev); Nrf24_configRegister(dev, CONFIG, mirf_CONFIG | ( (1 << PWR_UP) | (1 << PRIM_RX) ) ); //set device as TX mode Nrf24_ceHi(dev); Nrf24_configRegister(dev, STATUS, (1 << TX_DS) | (1 << MAX_RT)); //Clear seeded interrupt and max tx number interrupt } void Nrf24_flushRx(NRF24_t * dev) { spi_csnLow(dev); spi_transfer(dev, FLUSH_RX ); spi_csnHi(dev); } void Nrf24_powerUpTx(NRF24_t * dev) { dev->PTX = 1; Nrf24_configRegister(dev, CONFIG, mirf_CONFIG | ( (1 << PWR_UP) | (0 << PRIM_RX) ) ); } void Nrf24_ceHi(NRF24_t * dev) { gpio_set_level( dev->cePin, 1 ); } void Nrf24_ceLow(NRF24_t * dev) { gpio_set_level( dev->cePin, 0 ); } void Nrf24_powerDown(NRF24_t * dev) { Nrf24_ceLow(dev); Nrf24_configRegister(dev, CONFIG, mirf_CONFIG ); } //Set tx power : 0=-18dBm,1=-12dBm,2=-6dBm,3=0dBm void Nrf24_SetOutputRF_PWR(NRF24_t * dev, uint8_t val) { if (val > 3) return; uint8_t value; Nrf24_readRegister(dev, RF_SETUP, &value, 1); value = value & 0xF9; value = value | (val<< RF_PWR); //Nrf24_configRegister(dev, RF_SETUP, (val<< RF_PWR) ); Nrf24_configRegister(dev, RF_SETUP, value); } //Select between the high speed data rates:0=1Mbps, 1=2Mbps, 2=250Kbps void Nrf24_SetSpeedDataRates(NRF24_t * dev, uint8_t val) { if (val > 2) return; uint8_t value; Nrf24_readRegister(dev, RF_SETUP, &value, 1); if(val == 2) { value = value | 0x20; value = value & 0xF7; //Nrf24_configRegister(dev, RF_SETUP, (1 << RF_DR_LOW) ); Nrf24_configRegister(dev, RF_SETUP, value); } else { value = value & 0xD7; value = value | (val << RF_DR_HIGH); //Nrf24_configRegister(dev, RF_SETUP, (val << RF_DR_HIGH) ); Nrf24_configRegister(dev, RF_SETUP, value); } } //Set Auto Retransmit Delay 0=250us, 1=500us, ... 15=4000us void Nrf24_setRetransmitDelay(NRF24_t * dev, uint8_t val) { uint8_t value; Nrf24_readRegister(dev, SETUP_RETR, &value, 1); value = value & 0x0F; value = value | (val << ARD); Nrf24_configRegister(dev, SETUP_RETR, value); } void Nrf24_printDetails(NRF24_t * dev) { printf("================ SPI Configuration ================\n" ); printf("CSN Pin \t = GPIO%d\n",dev->csnPin); printf("CE Pin \t = GPIO%d\n", dev->cePin); printf("Clock Speed\t = %d\n", SPI_Frequency); printf("================ NRF Configuration ================\n"); Nrf24_print_status(Nrf24_getStatus(dev)); Nrf24_print_address_register(dev, "RX_ADDR_P0-1", RX_ADDR_P0, 2); Nrf24_print_byte_register(dev, "RX_ADDR_P2-5", RX_ADDR_P2, 4); Nrf24_print_address_register(dev, "TX_ADDR\t", TX_ADDR, 1); Nrf24_print_byte_register(dev, "RX_PW_P0-6", RX_PW_P0, 6); Nrf24_print_byte_register(dev, "EN_AA\t", EN_AA, 1); Nrf24_print_byte_register(dev, "EN_RXADDR", EN_RXADDR, 1); Nrf24_print_byte_register(dev, "RF_CH\t", RF_CH, 1); Nrf24_print_byte_register(dev, "RF_SETUP", RF_SETUP, 1); Nrf24_print_byte_register(dev, "CONFIG\t", CONFIG, 1); Nrf24_print_byte_register(dev, "DYNPD/FEATURE", DYNPD, 2); //printf("getDataRate()=%d\n",Nrf24_getDataRate(dev)); printf("Data Rate\t = %s\n",rf24_datarates[Nrf24_getDataRate(dev)]); #if 0 printf_P(PSTR("Model\t\t = " PRIPSTR "\r\n"),pgm_read_ptr(&rf24_model_e_str_P[isPVariant()])); #endif //printf("getCRCLength()=%d\n",Nrf24_getCRCLength(dev)); printf("CRC Length\t = %s\n", rf24_crclength[Nrf24_getCRCLength(dev)]); //printf("getPALevel()=%d\n",Nrf24_getPALevel(dev)); printf("PA Power\t = %s\n", rf24_pa_dbm[Nrf24_getPALevel(dev)]); uint8_t retransmit = Nrf24_getRetransmitDelay(dev); int16_t delay = (retransmit+1)*250; printf("Retransmit\t = %d us\n", delay); } #define _BV(x) (1<<(x)) void Nrf24_print_status(uint8_t status) { printf("STATUS\t\t = 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n", status, (status & _BV(RX_DR)) ? 1 : 0, (status & _BV(TX_DS)) ? 1 : 0, (status & _BV(MAX_RT)) ? 1 : 0, ((status >> RX_P_NO) & 0x07), (status & _BV(TX_FULL)) ? 1 : 0); } void Nrf24_print_address_register(NRF24_t * dev, const char* name, uint8_t reg, uint8_t qty) { printf("%s\t =",name); while (qty--) { //uint8_t buffer[addr_width]; uint8_t buffer[5]; Nrf24_readRegister(dev, reg++, buffer, sizeof(buffer)); printf(" 0x"); #if 0 uint8_t* bufptr = buffer + sizeof buffer; while (--bufptr >= buffer) { printf("%02x", *bufptr); } #endif for(int i=0;i<5;i++) { printf("%02x", buffer[i]); } } printf("\r\n"); } void Nrf24_print_byte_register(NRF24_t * dev, const char* name, uint8_t reg, uint8_t qty) { printf("%s\t =", name); while (qty--) { uint8_t buffer[1]; Nrf24_readRegister(dev, reg++, buffer, 1); printf(" 0x%02x", buffer[0]); } printf("\r\n"); } uint8_t Nrf24_getDataRate(NRF24_t * dev) { rf24_datarate_e result; uint8_t dr; Nrf24_readRegister(dev, RF_SETUP, &dr, sizeof(dr)); //printf("RF_SETUP=%x\n",dr); dr = dr & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)); // switch uses RAM (evil!) // Order matters in our case below if (dr == _BV(RF_DR_LOW)) { // '10' = 250KBPS result = RF24_250KBPS; } else if (dr == _BV(RF_DR_HIGH)) { // '01' = 2MBPS result = RF24_2MBPS; } else { // '00' = 1MBPS result = RF24_1MBPS; } return result; } uint8_t Nrf24_getCRCLength(NRF24_t * dev) { rf24_crclength_e result = RF24_CRC_DISABLED; uint8_t config; Nrf24_readRegister(dev, CONFIG, &config, sizeof(config)); //printf("CONFIG=%x\n",config); config = config & (_BV(CRCO) | _BV(EN_CRC)); uint8_t AA; Nrf24_readRegister(dev, EN_AA, &AA, sizeof(AA)); if (config & _BV(EN_CRC) || AA) { if (config & _BV(CRCO)) { result = RF24_CRC_16; } else { result = RF24_CRC_8; } } return result; } uint8_t Nrf24_getPALevel(NRF24_t * dev) { uint8_t level; Nrf24_readRegister(dev, RF_SETUP, &level, sizeof(level)); //printf("RF_SETUP=%x\n",level); level = (level & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1; return (level); } uint8_t Nrf24_getRetransmitDelay(NRF24_t * dev) { uint8_t value; Nrf24_readRegister(dev, SETUP_RETR, &value, 1); return (value >> 4); } I'm getting the following output: ... W (19498) NRF24: Maximum number of TX retries interrupt W (19498) TRANSMITTER: sending failed ... ... Update: Transmission succeeds but audio is not inteligible - 02/2023 I found out that I'm using Si24R1 (Chinese clone of nRF24L01) and I'm still using the i2s-adc esp-idf lib functions to get data from an analog microphone and I need to get this I2S stream through the SPI interface between the ESP32 and the nRF24L01 breakout board clone. Is this possible? I've seen some people mention on the internet that Nordic Semiconductors has nRF chips that support I2S but I would rather stick to the hardware I have available. Here's the link I've consulted. I've also found this library that supports audio transmission using the nRF24L01/Si24R1 chip using the nrf24.h lib but it is writeen in c++ and it is not clear to me how I could integrate it into my project. Let me know what you think the problem is or/and if you have good recommendations for learning material about this subject. Thanks for the help in advance!
Do your hardware works with a simple sender/receiver from sample codes you can find in adruino ide? If you didn't checked, have a try so you can validate the hardware is ok. If simple sender/receiver doesn't work, check your power supply lines. Sometimes not enough current from the voltage regulator. Add some caps on the rf24 module power-supply pins to filter some noise can also helps depending on the quality of the power supply. ./X
CAN Bus with MCC Microchip
I am trying to transmit through CAN using dsPIC33EV256GM106 and MCC to a raspberry Pi. I am new to CAN Bus and in c. I have configured CAN Bus and DMA via MCC, and I have called the functions in main.c. but nothing arrives in the Raspberry Pi. Here is my main.c, can.c and some pictures of MCC. If anyone has any ideas, how I can change the code to make the CAN bus work, I would appreciate any help.CAN Bus configuration DMA System_Module #include "mcc_generated_files/system.h" #include "mcc_generated_files/can_types.h" #include <stdio.h> /* Main application */ uCAN_MSG msg; int main(void) { // initialize the device SYSTEM_Initialize(); CAN1_TransmitEnable(); CAN1_ReceiveEnable(); CAN1_OperationModeSet(CAN_CONFIGURATION_MODE); msg.frame.id = 0x123; msg.frame.idType = CAN_FRAME_STD; msg.frame.msgtype = CAN_MSG_DATA; msg.frame.dlc = 0x08; msg.frame.data0 = 0x01; msg.frame.data1 = 0x02; msg.frame.data2 = 0x03; msg.frame.data3 = 0x04; msg.frame.data4 = 0x05; msg.frame.data5 = 0x06; msg.frame.data6 = 0x07; msg.frame.data7 = 0x08; while (1) { CAN1_Transmit(CAN_PRIORITY_HIGH, &msg); } return 1; } The pictures show the configuration of DMA and timer in MCC. enter image description here enter image description here #include "can1.h" #include "dma.h" #define CAN1_TX_DMA_CHANNEL DMA_CHANNEL_0 #define CAN1_RX_DMA_CHANNEL DMA_CHANNEL_2 /* Valid options are 4, 6, 8, 12, 16, 24, or 32. */ #define CAN1_MESSAGE_BUFFERS 32 #define CAN1_FIFO_STARTING_BUFFER 0x1 #define CAN1_TX_BUFFER_COUNT 1 #define CAN1_RX_BUFFER_MSG_DATA_SIZE 8U // CAN RX Buffer Message object data field size /* Private type definitions */ /******************************************************************************/ typedef struct __attribute__((packed)) { unsigned priority :2; unsigned remote_transmit_enable :1; unsigned send_request :1; unsigned error :1; unsigned lost_arbitration :1; unsigned message_aborted :1; unsigned transmit_enabled :1; } CAN1_TX_CONTROLS; /** Section: Private Variable Definitions */ /* This alignment is required because of the DMA's peripheral indirect * addressing mode. */ static unsigned int can1msgBuf [CAN1_MESSAGE_BUFFERS][8] __attribute__((aligned(32 * 8 * 2))); static void CAN1_DMACopy(uint8_t buffer_number, CAN_MSG_OBJ *message); static void CAN1_MessageToBuffer(uint16_t* buffer, CAN_MSG_OBJ *message); // CAN1 Default Interrupt Handler static void (*CAN1_BusErrorHandler)(void) = NULL; static void (*CAN1_TxErrorPassiveHandler)(void) = NULL; static void (*CAN1_RxErrorPassiveHandler)(void) = NULL; static void (*CAN1_BusWakeUpActivityInterruptHandler)(void) = NULL; static void (*CAN1_RxBufferInterruptHandler)(void) = NULL; static void (*CAN1_RxBufferOverFlowInterruptHandler)(void) = NULL; /** #Summary Read the message object from Receive buffer and update to the user message object pointer. #Description This routine read the message object from Receive buffer and update to the user message object pointer. #Preconditions CAN1_Initialize function should be called before calling this function. #Param bufferNumber - A buffer number is in the Receive buffer where the message would be stored. message - pointer to the CAN1 Receive message object. #Returns None #Example None */ static void CAN1_DMACopy(uint8_t buffer_number, CAN_MSG_OBJ *message) { uint16_t ide=0; uint16_t rtr=0; uint32_t id=0; /* read word 0 to see the message type */ ide=can1msgBuf[buffer_number][0] & 0x0001U; /* check to see what type of message it is */ /* message is standard identifier */ if(ide==0U) { message->msgId =(can1msgBuf[buffer_number][0] & 0x1FFCU) >> 2U; message->field.idType = CAN_FRAME_STD; rtr=can1msgBuf[buffer_number][0] & 0x0002U; } /* message is extended identifier */ else { id=can1msgBuf[buffer_number][0] & 0x1FFCU; message->msgId = id << 16U; message->msgId += ( ((uint32_t) can1msgBuf[buffer_number][1] & (uint32_t)0x0FFF) << 6U ); message->msgId += ( ((uint32_t) can1msgBuf[buffer_number][2] & (uint32_t)0xFC00U) >> 10U ); message->field.idType = CAN_FRAME_EXT; rtr=can1msgBuf[buffer_number][2] & 0x0200; } /* check to see what type of message it is */ /* RTR message */ if(rtr != 0U) { /* to be defined ?*/ message->field.frameType = CAN_FRAME_RTR; } /* normal message */ else { message->field.frameType = CAN_FRAME_DATA; message->data[0] =(uint8_t) can1msgBuf[buffer_number][3]; message->data[1] =(uint8_t) ((can1msgBuf[buffer_number][3] & 0xFF00U) >> 8U); message->data[2] =(uint8_t) can1msgBuf[buffer_number][4]; message->data[3] =(uint8_t) ((can1msgBuf[buffer_number][4] & 0xFF00U) >> 8U); message->data[4] =(uint8_t) can1msgBuf[buffer_number][5]; message->data[5] =(uint8_t) ((can1msgBuf[buffer_number][5] & 0xFF00U) >> 8U); message->data[6] =(uint8_t) can1msgBuf[buffer_number][6]; message->data[7] =(uint8_t) ((can1msgBuf[buffer_number][6] & 0xFF00U) >> 8U); message->field.dlc =(uint8_t) (can1msgBuf[buffer_number][2] & 0x000FU); } } /** #Summary Read the message object from user input and update to the CAN1 TX buffer. #Description This routine Read the message object from user input and update to the CAN1 TX buffer. #Preconditions CAN1_Initialize function should be called before calling this function. #Param buffer - pointer to the CAN1 Message object. message - pointer to the CAN1 transmit message object. #Returns None #Example None */ static void CAN1_MessageToBuffer(uint16_t* buffer, CAN_MSG_OBJ* message) { if(message->field.idType == CAN_FRAME_STD) { buffer[0]= (message->msgId & 0x000007FF) << 2; buffer[1]= 0; buffer[2]= message->field.dlc & 0x0F; } else { buffer[0]= ( ( (uint16_t)(message->msgId >> 16 ) & 0x1FFC ) ) | 0x3; buffer[1]= (uint16_t)(message->msgId >> 6) & 0x0FFF; buffer[2]= (message->field.dlc & 0x0F) + ( (uint16_t)(message->msgId << 10) & 0xFC00); } if(message->data != NULL) { buffer[3]= ((message->data[1])<<8) + message->data[0]; buffer[4]= ((message->data[3])<<8) + message->data[2]; buffer[5]= ((message->data[5])<<8) + message->data[4]; buffer[6]= ((message->data[7])<<8) + message->data[6]; } } /** Section: CAN1 APIs */ void CAN1_Initialize(void) { // Disable interrupts before the Initialization IEC2bits.C1IE = 0; C1INTE = 0; // set the CAN1_initialize module to the options selected in the User Interface /* put the module in configuration mode */ C1CTRL1bits.REQOP = CAN_CONFIGURATION_MODE; while(C1CTRL1bits.OPMODE != CAN_CONFIGURATION_MODE); /* Set up the baud rate*/ C1CFG1 = 0x03; //BRP TQ = (2 x 4)/FCAN; SJW 1 x TQ; C1CFG2 = 0x43BE; //WAKFIL enabled; SEG2PHTS Freely programmable; SEG2PH 4 x TQ; SEG1PH 8 x TQ; PRSEG 7 x TQ; SAM Once at the sample point; C1FCTRL = 0xC001; //FSA Transmit/Receive Buffer TRB1; DMABS 32; C1FEN1 = 0x01; //FLTEN8 disabled; FLTEN7 disabled; FLTEN9 disabled; FLTEN0 enabled; FLTEN2 disabled; FLTEN10 disabled; FLTEN1 disabled; FLTEN11 disabled; FLTEN4 disabled; FLTEN3 disabled; FLTEN6 disabled; FLTEN5 disabled; FLTEN12 disabled; FLTEN13 disabled; FLTEN14 disabled; FLTEN15 disabled; C1CTRL1 = 0x00; //CANCKS FOSC/2; CSIDL disabled; ABAT disabled; REQOP Sets Normal Operation Mode; WIN Uses buffer window; CANCAP disabled; /* Filter configuration */ /* enable window to access the filter configuration registers */ /* use filter window*/ C1CTRL1bits.WIN=1; /* select acceptance masks for filters */ C1FMSKSEL1bits.F0MSK = 0x0; //Select Mask 0 for Filter 0 /* Configure the masks */ C1RXM0SIDbits.SID = 0x7ff; C1RXM1SIDbits.SID = 0x0; C1RXM2SIDbits.SID = 0x0; C1RXM0SIDbits.EID = 0x0; C1RXM1SIDbits.EID = 0x0; C1RXM2SIDbits.EID = 0x0; C1RXM0EID = 0x00; C1RXM1EID = 0x00; C1RXM2EID = 0x00; C1RXM0SIDbits.MIDE = 0x0; C1RXM1SIDbits.MIDE = 0x0; C1RXM2SIDbits.MIDE = 0x0; /* Configure the filters */ C1RXF0SIDbits.SID = 0x123; C1RXF0SIDbits.EID = 0x0; C1RXF0EID = 0x00; C1RXF0SIDbits.EXIDE = 0x0; /* FIFO Mode */ C1BUFPNT1bits.F0BP = 0xf; //Filter 0 uses FIFO /* clear window bit to access CAN1 control registers */ C1CTRL1bits.WIN=0; /*configure CAN1 Transmit/Receive buffer settings*/ C1TR01CONbits.TXEN0 = 0x1; // Buffer 0 is a Transmit Buffer C1TR01CONbits.TXEN1 = 0x0; // Buffer 1 is a Receive Buffer C1TR23CONbits.TXEN2 = 0x0; // Buffer 2 is a Receive Buffer C1TR23CONbits.TXEN3 = 0x0; // Buffer 3 is a Receive Buffer C1TR45CONbits.TXEN4 = 0x0; // Buffer 4 is a Receive Buffer C1TR45CONbits.TXEN5 = 0x0; // Buffer 5 is a Receive Buffer C1TR67CONbits.TXEN6 = 0x0; // Buffer 6 is a Receive Buffer C1TR67CONbits.TXEN7 = 0x0; // Buffer 7 is a Receive Buffer C1TR01CONbits.TX0PRI = 0x0; // Message Buffer 0 Priority Level C1TR01CONbits.TX1PRI = 0x0; // Message Buffer 1 Priority Level C1TR23CONbits.TX2PRI = 0x0; // Message Buffer 2 Priority Level C1TR23CONbits.TX3PRI = 0x0; // Message Buffer 3 Priority Level C1TR45CONbits.TX4PRI = 0x0; // Message Buffer 4 Priority Level C1TR45CONbits.TX5PRI = 0x0; // Message Buffer 5 Priority Level C1TR67CONbits.TX6PRI = 0x0; // Message Buffer 6 Priority Level C1TR67CONbits.TX7PRI = 0x0; // Message Buffer 7 Priority Level /* clear the buffer and overflow flags */ C1RXFUL1 = 0x0000; C1RXFUL2 = 0x0000; C1RXOVF1 = 0x0000; C1RXOVF2 = 0x0000; /* configure the device to interrupt on the receive buffer full flag */ /* clear the buffer full flags */ C1INTFbits.RBIF = 0; /* put the module in normal mode */ C1CTRL1bits.REQOP = CAN_NORMAL_OPERATION_MODE; while(C1CTRL1bits.OPMODE != CAN_NORMAL_OPERATION_MODE); /* Initialize Interrupt Handler*/ CAN1_SetBusErrorHandler(&CAN1_DefaultBusErrorHandler); CAN1_SetTxErrorPassiveHandler(&CAN1_DefaultTxErrorPassiveHandler); CAN1_SetRxErrorPassiveHandler(&CAN1_DefaultRxErrorPassiveHandler); CAN1_SetBusWakeUpActivityInterruptHandler(&CAN1_DefaultBusWakeUpActivityHandler); CAN1_SetRxBufferInterruptHandler(&CAN1_DefaultReceiveBufferHandler); CAN1_SetRxBufferOverFlowInterruptHandler(&CAN1_DefaultRxBufferOverFlowHandler); /* Enable CAN1 Interrupt */ IEC2bits.C1IE = 1; /* Enable Receive interrupt */ C1INTEbits.RBIE = 1; /* Enable Error interrupt*/ C1INTEbits.ERRIE = 1; /* Enable Receive buffer Overflow interrupt */ C1INTEbits.RBOVIE = 1; } void CAN1_TransmitEnable() { /* setup channel 0 for peripheral indirect addressing mode normal operation, word operation and select as Tx to peripheral */ /* DMA_PeripheralIrqNumberSet and DMA_TransferCountSet would be done in the DMA */ /* setup the address of the peripheral CAN1 (C1TXD) */ DMA_PeripheralAddressSet(CAN1_TX_DMA_CHANNEL, (uint16_t) &C1TXD); /* DPSRAM start address offset value */ DMA_StartAddressASet(CAN1_TX_DMA_CHANNEL, (uint16_t) (&can1msgBuf)); /* enable the channel */ DMA_ChannelEnable(CAN1_TX_DMA_CHANNEL); } void CAN1_ReceiveEnable() { /* setup DMA channel for peripheral indirect addressing mode normal operation, word operation and select as Rx to peripheral */ /* setup the address of the peripheral CAN1 (C1RXD) */ /* DMA_TransferCountSet and DMA_PeripheralIrqNumberSet would be set in the DMA_Initialize function */ DMA_PeripheralAddressSet(CAN1_RX_DMA_CHANNEL, (uint16_t) &C1RXD); /* DPSRAM start address offset value */ DMA_StartAddressASet(CAN1_RX_DMA_CHANNEL, (uint16_t) (&can1msgBuf) ); /* enable the channel */ DMA_ChannelEnable(CAN1_RX_DMA_CHANNEL); } CAN_OP_MODE_STATUS CAN1_OperationModeSet(const CAN_OP_MODES requestMode) { CAN_OP_MODE_STATUS status = CAN_OP_MODE_REQUEST_SUCCESS; if((CAN_CONFIGURATION_MODE == CAN1_OperationModeGet()) || (requestMode == CAN_DISABLE_MODE) || (requestMode == CAN_CONFIGURATION_MODE)) { C1CTRL1bits.REQOP = requestMode; while(C1CTRL1bits.OPMODE != requestMode); } else { status = CAN_OP_MODE_REQUEST_FAIL; } return status; } CAN_OP_MODES CAN1_OperationModeGet(void) { return C1CTRL1bits.OPMODE; } CAN_TX_MSG_REQUEST_STATUS CAN1_Transmit(CAN_TX_PRIOIRTY priority, CAN_MSG_OBJ *sendCanMsg) { CAN_TX_MSG_REQUEST_STATUS txMsgStatus = CAN_TX_MSG_REQUEST_SUCCESS; CAN1_TX_CONTROLS * pTxControls = (CAN1_TX_CONTROLS*)&C1TR01CON; uint_fast8_t i; bool messageSent = false; // CAN 2.0 mode DLC supports upto 8 byte if(sendCanMsg->field.dlc > CAN_DLC_8) { txMsgStatus |= CAN_TX_MSG_REQUEST_DLC_ERROR; } if(CAN1_TX_BUFFER_COUNT > 0) { for(i=0; i<CAN1_TX_BUFFER_COUNT; i++) { if(pTxControls->transmit_enabled == 1) { if (pTxControls->send_request == 0) { CAN1_MessageToBuffer(&can1msgBuf[i][0], sendCanMsg); pTxControls->priority = priority; /* set the message for transmission */ pTxControls->send_request = 1; messageSent = true; break; } } pTxControls++; } } if(messageSent == false) { txMsgStatus |= CAN_TX_MSG_REQUEST_BUFFER_FULL; } return txMsgStatus; } bool CAN1_Receive(CAN_MSG_OBJ *recCanMsg) { uint_fast8_t currentBuffer; uint_fast8_t shiftAmount; bool messageReceived = false; uint16_t receptionFlags; if(C1INTFbits.RBOVIF == 1) { C1INTFbits.RBOVIF = 0; /* Receive buffer overflow occured, call the notification function */ if(CAN1_RxBufferOverFlowInterruptHandler) { CAN1_RxBufferOverFlowInterruptHandler(); } return messageReceived; } if(recCanMsg->data == NULL) { return messageReceived; } currentBuffer = C1FIFObits.FNRB; if( currentBuffer < 16) { receptionFlags = C1RXFUL1; shiftAmount = currentBuffer; } else { receptionFlags = C1RXFUL2; shiftAmount = currentBuffer - 16; } if (((receptionFlags >> shiftAmount ) & 0x1) == 0x1) { CAN1_DMACopy(currentBuffer, recCanMsg); if( currentBuffer < 16) { C1RXFUL1 &= ~(1 << shiftAmount); } else { C1RXFUL2 &= ~(1 << shiftAmount); } messageReceived = true; } return (messageReceived); } bool CAN1_IsBusOff() { return C1INTFbits.TXBO; } bool CAN1_IsRXErrorPassive() { return (C1INTFbits.RXBP); } bool CAN1_IsRxErrorWarning(void) { return (C1INTFbits.RXWAR); } bool CAN1_IsRxErrorActive(void) { bool errorState = false; if((0 < C1ECbits.RERRCNT) && (C1ECbits.RERRCNT < 128)) { errorState = true; } return errorState; } bool CAN1_IsTXErrorPassive() { return (C1INTFbits.TXBP); } bool CAN1_IsTxErrorWarning(void) { return (C1INTFbits.TXWAR); } bool CAN1_IsTxErrorActive(void) { bool errorState = false; if((0 < C1ECbits.TERRCNT) && (C1ECbits.TERRCNT < 128)) { errorState = true; } return errorState; } uint8_t CAN1_ReceivedMessageCountGet() { uint_fast8_t messageCount; uint_fast8_t currentBuffer; uint16_t receptionFlags; messageCount = 0; #if (CAN1_FIFO_STARTING_BUFFER<16) /* Check any message in buffer 0 to buffer 15*/ receptionFlags = C1RXFUL1; if (receptionFlags != 0) { /* check whether a message is received */ for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++) { if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1) { messageCount++; } } } #endif /* Check any message in buffer 16 to buffer 32*/ receptionFlags = C1RXFUL2; if (receptionFlags != 0) { /* check whether a message is received */ for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++) { if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1) { messageCount++; } } } return (messageCount); } void CAN1_Sleep(void) { C1INTFbits.WAKIF = 0; C1INTEbits.WAKIE = 1; /* put the module in disable mode */ C1CTRL1bits.REQOP = CAN_DISABLE_MODE; while(C1CTRL1bits.OPMODE != CAN_DISABLE_MODE); //Wake up from sleep should set the CAN1 module straight into Normal mode } void __attribute__((weak)) CAN1_DefaultBusErrorHandler(void) { CAN1_CallbackBusOff(); } void CAN1_SetBusErrorHandler(void *handler) { CAN1_BusErrorHandler = handler; } void __attribute__((weak)) CAN1_DefaultTxErrorPassiveHandler(void) { CAN1_CallbackTxErrorPassive(); } void CAN1_SetTxErrorPassiveHandler(void *handler) { CAN1_TxErrorPassiveHandler = handler; } void __attribute__((weak)) CAN1_DefaultRxErrorPassiveHandler(void) { CAN1_CallbackRxErrorPassive(); } void CAN1_SetRxErrorPassiveHandler(void *handler) { CAN1_RxErrorPassiveHandler = handler; } void __attribute__((weak)) CAN1_DefaultBusWakeUpActivityHandler(void) { } void CAN1_SetBusWakeUpActivityInterruptHandler(void *handler) { CAN1_BusWakeUpActivityInterruptHandler = handler; } void __attribute__((weak)) CAN1_DefaultReceiveBufferHandler(void) { CAN1_CallbackMessageReceived(); } void CAN1_SetRxBufferInterruptHandler(void *handler) { CAN1_RxBufferInterruptHandler = handler; } void __attribute__((weak)) CAN1_DefaultRxBufferOverFlowHandler(void) { CAN1_CallbackRxBufferOverflow(); } void CAN1_SetRxBufferOverFlowInterruptHandler(void *handler) { CAN1_RxBufferOverFlowInterruptHandler = handler; } void __attribute__((__interrupt__, no_auto_psv)) _C1Interrupt(void) { if (C1INTFbits.ERRIF) { if (C1INTFbits.TXBO == 1) { if(CAN1_BusErrorHandler) { CAN1_BusErrorHandler(); } } if (C1INTFbits.TXBP == 1) { if(CAN1_TxErrorPassiveHandler) { CAN1_TxErrorPassiveHandler(); } } if (C1INTFbits.RXBP == 1) { if(CAN1_RxErrorPassiveHandler) { CAN1_RxErrorPassiveHandler(); } } /* Call error notification function */ C1INTFbits.ERRIF = 0; } if(C1INTFbits.RBIF) { if(CAN1_RxBufferInterruptHandler) { CAN1_RxBufferInterruptHandler(); } C1INTFbits.RBIF = 0; } if(C1INTFbits.WAKIF) { if(CAN1_BusWakeUpActivityInterruptHandler) { CAN1_BusWakeUpActivityInterruptHandler(); } C1INTFbits.WAKIF = 0; } IFS2bits.C1IF = 0; } /******************************************************************************* !!! Deprecated Definitions and APIs !!! !!! These functions will not be supported in future releases !!! *******************************************************************************/ /****************************************************************************** * * Function: CAN1_transmit * Description: Transmits the message from user buffer to CAN1 buffer * as per the buffer number allocated. * Allocation of the buffer number is done by user * * Arguments: priority : priority of the message to be transmitted * sendCanMsg: pointer to the message object * * Return Value: true - Transmit successful * false - Transmit failure ******************************************************************************/ bool CAN1_transmit(CAN_TX_PRIOIRTY priority, uCAN_MSG *sendCanMsg) { uint8_t msgObjData[8] = {0}; CAN_MSG_OBJ txCanMsg; txCanMsg.data = msgObjData; txCanMsg.msgId = sendCanMsg->frame.id; txCanMsg.field.idType = sendCanMsg->frame.idType; txCanMsg.field.frameType = sendCanMsg->frame.msgtype; txCanMsg.field.dlc = sendCanMsg->frame.dlc; txCanMsg.data[0] = sendCanMsg->frame.data0; txCanMsg.data[1] = sendCanMsg->frame.data1; txCanMsg.data[2] = sendCanMsg->frame.data2; txCanMsg.data[3] = sendCanMsg->frame.data3; txCanMsg.data[4] = sendCanMsg->frame.data4; txCanMsg.data[5] = sendCanMsg->frame.data5; txCanMsg.data[6] = sendCanMsg->frame.data6; txCanMsg.data[7] = sendCanMsg->frame.data7; return (CAN1_Transmit(priority, &txCanMsg)); } /****************************************************************************** * * Function: CAN1_receive * Description: Receives the message from CAN1 buffer to user buffer * * Arguments: recCanMsg: pointer to the message object * * Return Value: true - Receive successful * false - Receive failure ******************************************************************************/ bool CAN1_receive(uCAN_MSG *recCanMsg) { bool messageReceived = false; uint8_t msgObjData[8] = {0}; CAN_MSG_OBJ rxCanMsg; rxCanMsg.data = msgObjData; if(true == CAN1_Receive(&rxCanMsg)) { recCanMsg->frame.id = rxCanMsg.msgId; recCanMsg->frame.idType = rxCanMsg.field.idType; if(rxCanMsg.field.frameType == CAN_FRAME_RTR) { recCanMsg->frame.msgtype = CAN_MSG_RTR; } else { recCanMsg->frame.msgtype = CAN_MSG_DATA; } recCanMsg->frame.data0 = rxCanMsg.data[0]; recCanMsg->frame.data1 = rxCanMsg.data[1]; recCanMsg->frame.data2 = rxCanMsg.data[2]; recCanMsg->frame.data3 = rxCanMsg.data[3]; recCanMsg->frame.data4 = rxCanMsg.data[4]; recCanMsg->frame.data5 = rxCanMsg.data[5]; recCanMsg->frame.data6 = rxCanMsg.data[6]; recCanMsg->frame.data7 = rxCanMsg.data[7]; recCanMsg->frame.dlc = rxCanMsg.field.dlc; messageReceived = true; } return (messageReceived); } /****************************************************************************** * * Function: CAN1_isBusOff * Description: Checks whether the transmitter in Bus off state * * Return Value: true - Transmitter in Bus Off state * false - Transmitter not in Bus Off state ******************************************************************************/ bool CAN1_isBusOff() { return C1INTFbits.TXBO; } /****************************************************************************** * * Function: CAN1_isRXErrorPassive * Description: Checks whether the receive in error passive state * * Return Value: true - Receiver in Error Passive state * false - Receiver not in Error Passive state ******************************************************************************/ bool CAN1_isRXErrorPassive() { return C1INTFbits.RXBP; } /****************************************************************************** * * Function: CAN1_isTXErrorPassive * Description: Checks whether the transmitter in error passive state * * Return Value: true - Transmitter in Error Passive state * false - Transmitter not in Error Passive state ******************************************************************************/ bool CAN1_isTXErrorPassive() { return (C1INTFbits.TXBP); } /****************************************************************************** * * Function: CAN1_messagesInBuffer * Description: returns the number of messages that are received * * Return Value: Number of message received ******************************************************************************/ uint8_t CAN1_messagesInBuffer() { uint_fast8_t messageCount; uint_fast8_t currentBuffer; uint16_t receptionFlags; messageCount = 0; #if (CAN1_FIFO_STARTING_BUFFER<16) /* Check any message in buffer 0 to buffer 15*/ receptionFlags = C1RXFUL1; if (receptionFlags != 0) { /* check whether a message is received */ for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++) { if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1) { messageCount++; } } } #endif /* Check any message in buffer 16 to buffer 32*/ receptionFlags = C1RXFUL2; if (receptionFlags != 0) { /* check whether a message is received */ for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++) { if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1) { messageCount++; } } } return (messageCount); } /****************************************************************************** * * Function: CAN1_sleep * Description: Puts CAN1 module in disable mode. * ******************************************************************************/ void CAN1_sleep(void) { C1INTFbits.WAKIF = 0; C1INTEbits.WAKIE = 1; /* put the module in disable mode */ C1CTRL1bits.REQOP = CAN_DISABLE_MODE; while(C1CTRL1bits.OPMODE != CAN_DISABLE_MODE); //Wake up from sleep should set the CAN1 module straight into Normal mode } /* Null weak implementations of callback functions. */ void __attribute__((weak)) CAN1_CallbackBusOff(void) { } void __attribute__((weak)) CAN1_CallbackTxErrorPassive(void) { } void __attribute__((weak)) CAN1_CallbackRxErrorPassive(void) { } void __attribute__((weak)) CAN1_CallbackMessageReceived(void) { } void __attribute__((weak)) CAN1_CallbackRxBufferOverflow() { } /** End of File */
Segmentation Failure with print and write instructions, in Implementation between Fortran and C
I'm trying to call a C function from Fortran, using the iso_c_binding interoperability. However, I am getting a SegFault error when trying to use print and write statements. Without the print and write statements the code works fine, but I need these statements to create an output file with the simulation data. Does anyone know how to solve this problem? Note: I am using Ubuntu 20.04, GFortran, and GCC to compile the respective source codes. gcc -c subroutine_in_c.c gfortran -o exec main.f90 subroutine_in_c.o -lwiringPi main.f90: PROGRAM main USE, INTRINSIC:: iso_c_binding, ONLY: C_FLOAT IMPLICIT NONE REAL(KIND = 4) :: leitura_sensor = 0.0 INTERFACE SUBROUTINE ler_sensores(s1) BIND(C) USE, INTRINSIC :: iso_c_binding, ONLY: C_FLOAT IMPLICIT NONE REAL(KIND=C_FLOAT) :: s1 END SUBROUTINE ler_sensores END INTERFACE !print*, 'Call subroutine in C language' call ler_sensores(leitura_sensor) !print*, 'Return to main.f90' OPEN(UNIT=1, FILE='output.txt', STATUS='unknown') WRITE(1,*) leitura_sensor CLOSE(UNIT=1) END PROGRAM main subroutine_in_c.c: #include <stdint.h> #include <string.h> #include <errno.h> #include <wiringPi.h> #include <stdio.h> #include <stdlib.h> #include <wiringPiSPI.h> #include <wiringPiI2C.h> #define LCDADDR 0x27 //IIC LCD address #define BLEN 1 //1--open backlight,0--close backlight #define CHAN_CONFIG_SINGLE 8 //setup channel 0 as Single-ended input #define SPICHANNEL 0 //MCP3008 connect to SPI0 #define ANALOGCHANNEL 0 //Potentiometer connect MCP3008 analog channel 0 #define ANALOGCHANNEL2 1 static int spifd; static int i2cfd; void spiSetup (int spiChannel) { if ((spifd = wiringPiSPISetup (spiChannel, 10000)) < 0) { fprintf (stderr, "Can't open the SPI bus: %s\n", strerror (errno)) ; exit (EXIT_FAILURE) ; } } int myAnalogRead(int spiChannel,int channelConfig,int analogChannel) { if (analogChannel<0 || analogChannel>7) return -1; unsigned char buffer[3] = {1}; // start bit buffer[1] = (channelConfig+analogChannel) << 4; wiringPiSPIDataRW(spiChannel, buffer, 3); return ( (buffer[1] & 3 ) << 8 ) + buffer[2]; // get last 10 bits } //write a word to lcd void write_word(int data) { int temp = data; if ( BLEN == 1 ) temp |= 0x08; else temp &= 0xF7; wiringPiI2CWrite(i2cfd, temp); } //send command to lcd void send_command(int comm) { int buf; // Send bit7-4 firstly buf = comm & 0xF0; buf |= 0x04; // RS = 0, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); // Send bit3-0 secondly buf = (comm & 0x0F) << 4; buf |= 0x04; // RS = 0, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); } //send data to lcd void send_data(int data) { int buf; // Send bit7-4 firstly buf = data & 0xF0; buf |= 0x05; // RS = 1, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); // Send bit3-0 secondly buf = (data & 0x0F) << 4; buf |= 0x05; // RS = 1, RW = 0, EN = 1 write_word(buf); delay(2); buf &= 0xFB; // Make EN = 0 write_word(buf); } //initialize the lcd void init() { send_command(0x33); // Must initialize to 8-line mode at first delay(5); send_command(0x32); // Then initialize to 4-line mode delay(5); send_command(0x28); // 2 Lines & 5*7 dots delay(5); send_command(0x0C); // Enable display without cursor delay(5); send_command(0x01); // Clear Screen wiringPiI2CWrite(i2cfd, 0x08); } //clear screen void clear() { send_command(0x01); //clear Screen } //Print the message on the lcd void write(int x, int y, char data[]) { int addr, i; int tmp; if (x < 0) x = 0; if (x > 15) x = 15; if (y < 0) y = 0; if (y > 1) y = 1; // Move cursor addr = 0x80 + 0x40 * y + x; send_command(addr); tmp = strlen(data); for (i = 0; i < tmp; i++) { send_data(data[i]); } } void ler_sensores(float *s1) { int adc; int adc2; int i; float voltage; float voltage2; char buf[5]; if (wiringPiSetup() < 0) { fprintf(stderr,"Can't init wiringPi: %s\n",strerror(errno)); exit(EXIT_FAILURE); } spiSetup(SPICHANNEL);//init spi i2cfd = wiringPiI2CSetup(LCDADDR); //init i2c init(); //init LCD clear(); //clear screen for (i = 0; i <25; i++) { adc = myAnalogRead(SPICHANNEL,CHAN_CONFIG_SINGLE,ANALOGCHANNEL); adc2 = myAnalogRead(SPICHANNEL, CHAN_CONFIG_SINGLE, ANALOGCHANNEL2); voltage = adc/1024.*20.0; write(0,0,"Ch Linear:"); sprintf(buf,"%2.2f",voltage);//float change to string write(10,0,buf);//print voltage on lcd write(15,0,"V");//print unit write(0,1,"Ch Logarit:"); voltage2 = adc2/1024.*20.0; sprintf(buf,"%2.2f",voltage2); write(11,1, buf); write(16,1,"V"); delay(1000); } *s1 = voltage; } Thank you in advance to everyone who helps.
This question probably deserves an answer even if the reason for the problem is the obscure one, identified by Craig Estay. Gfortran's runtime library, called when using the print and write statements, contains calls to write() and having another C function called write will cause the gfortran runtime to call a wrong function. It can easily be tested in a simple program like this: testwrite.c: #include "stdio.h" void write(){ puts("my C write"); } testwrite.f90: print *,"test print" write(*,*) "test write" end When using gfortran testwrite.c testwrite.f90, the output is: my C write my C write my C write my C write The same output appears when using icc testwrite.c -c -o c.o and ifort c.o testwrite.f90.
Error: identifier "LCD_E_PORT" is undefined
Why I get the error: identifier "LCD_E_PORT" is undefined in this code: #include <msp430f5438a.h> #include "IO_functions.h" #define LCD_E_PORT PORT_6 #define LCD_E PIN_4 #include "LCD1602.h" int main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer P4DIR = 0x03; P4OUT = 0x00; output_bit(PORT_4, PIN_7, 1); output_bit(PORT_4, PIN_7, 0); lcd_send_nibble(0x0f); while(1) { P4OUT ^= BIT0; __delay_cycles(500000); } } Since I have defined LCD_E_PORT at the top of my code I don't understand where this error comes from. This is LCD1602.c in which I am using LCD_E_PORT: #include <msp430f5438a.h> #include "LCD1602.h" #include "IO_functions.h" void lcd_send_nibble(unsigned char nibble) { output_bit(LCD_DB4_PORT, LCD_DB4, !!(nibble & 1)); output_bit(LCD_DB5_PORT, LCD_DB5, !!(nibble & 2)); output_bit(LCD_DB6_PORT, LCD_DB6, !!(nibble & 4)); output_bit(LCD_DB7_PORT, LCD_DB7, !!(nibble & 8)); __delay_cycles(8); output_bit(LCD_E_PORT, LCD_E, 1); __delay_cycles(16); output_bit(LCD_E_PORT, LCD_E, 0); } void lcd_send_byte(unsigned char data_instr, unsigned char data) { output_bit(LCD_RS_PORT, LCD_RS, 0); __delay_cycles(480); if(data_instr == DATA) output_bit(LCD_RS_PORT, LCD_RS, 1); else output_bit(LCD_RS_PORT, LCD_RS, 0); __delay_cycles(8); output_bit(LCD_E_PORT, LCD_E, 0); lcd_send_nibble(data >> 4); lcd_send_nibble(data & 0x0F); } void lcd_init(void) { unsigned char i; output_bit(LCD_RS_PORT, LCD_RS, 0); output_bit(LCD_E_PORT, LCD_E, 0); __delay_cycles(120000); for(i=0 ;i < 3; i++) { lcd_send_nibble(0x03); __delay_cycles(25000); } lcd_send_nibble(0x02); lcd_send_byte(INSTR, 0x28); __delay_cycles(25000); lcd_send_byte(INSTR, 0x0C); __delay_cycles(25000); lcd_send_byte(INSTR, 0x01); __delay_cycles(25000); lcd_send_byte(INSTR, 0x06); __delay_cycles(25000); }
As per the updated code, the definition of LCD_E_PORT is not visible from LCD1602.c file. You might want to add the #define in the header file (LCD1602.h or any other, at your choice) itself which will be included in all the source files making use of the macro.
How to change the transmission of bytes via the port
How to change the transmission of bytes via the port? Was: Interrupt. Need: According to the poll. plug-in -> http://pastie.org/3994352 Client: #include <stdio.h> #include <conio.h> #include <dos.h> #include "comm.h" // Connect module void main(void) { char cmd[128]; /* Set the COM port */ printf("Adjustable com-port ...\n"); OpenSerial(COM_1, SER_BAUD_9600, SER_STOP_2 | SER_BITS_8 | SER_PARITY_EVEN); printf("Submitting a request for a connection ...\n"); WriteSer(0xFF); while (1) { if (kbhit()) { int c = getche(); if (c == 13) putch(10); WriteSer(c); } if (DataReadyCount()) { int c = ReadQueue(); if (c == 0xFF) break; putch(c); } } printf("Ending a connection ...\n"); CloseSerial(); }
I found a sample on the web which may be of value, at least in showcasing the polling vs. interrupt driven approaches. Note that the interrupt method needs calls to setvect, outportb, saving the old interrupt and restoring it, etc. Don't ask about TSRs next. :) // http://ragestorm.net // serial communications example // interrupt driven/polled method #include <dos.h> #include <conio.h> #include <stdio.h> // serial port base addresses: #define COM1 (0x3f8) #define COM2 (0x2f8) // stack size for interrupt #define STACK_SIZE 1024 // serial ports registers: // receive buffer #define RBR (0x0) // transmitter hold #define THR (0x0) // interrupt enable #define IER (0x1) // interrupt identification #define IIR (0x2) // fifo control #define FCR (0x2) // line control #define LCR (0x3) // modem control #define MCR (0x4) // line status #define LSR (0x5) // modem status #define MSR (0x6) // scratch-pad #define SPR (0x7) // divisor lsb byte #define DIVLSB (0x0) // divisor msb byte #define DIVMSB (0x1) // possible irqs for com ports int com_irqs[4] = {4, 3, 4, 3}; // the com port addr being used int used_com_addr = 0; // the irq being used if interrupt driven int used_com_irq = 0; // interrupt driven or polling method? int intr_used = 0; // built in stack for interrupt usage unsigned char recv_stack[STACK_SIZE]; unsigned char* next_char = recv_stack; // old handler address void interrupt (*old_handler)(...) = NULL; void interrupt new_handler(...); // get com address from bios unsigned short get_com_addr(int com_no) { if ((com_no <= 0) || (com_no >= 5)) return -1; // bios seg addr unsigned char* biosaddr = (unsigned char *)0x400; // set irq according to com number used_com_irq = com_irqs[com_no - 1]; // retreive addresses bios return *(unsigned short*)&biosaddr[(com_no - 1) << 1]; } // detect the uart type of the used com prot addr // this is mainly to know if fifo is available. // 0: no uart found, 1: 8250, 2: 16450 or 8250(with spr), 3: 16550, 4: 16550A int detect_uart_type() { char old_data = 0; // check UART presentation by checking loopback mode old_data = inportb(used_com_addr + MCR); outportb(used_com_addr + MCR, 0x10); if ((inportb(used_com_addr + MSR) & 0xf0)) return 0; outportb(used_com_addr + MCR, 0x1f); if ((inportb(used_com_addr + MSR) & 0xf0) != 0xf0) return 0; outportb(used_com_addr + MCR, old_data); // write values to scratch pad and readback old_data = inportb(used_com_addr + SPR); outportb(used_com_addr + SPR, 0x55); if (inportb(used_com_addr + SPR) != 0x55) return 1; outportb(used_com_addr + SPR, 0xAA); if (inportb(used_com_addr + SPR) != 0xAA) return 1; outportb(used_com_addr + SPR, old_data); // enable fifo and determine version by part identification outportb(used_com_addr + FCR, 1); old_data = inportb(used_com_addr + FCR); outportb(used_com_addr + FCR, 0); if ((~old_data & 0x80)) return 2; // 16450 if ((~old_data & 0x40)) return 3; // 16550 return 4; // 16550a + } // inits the serial com port with a specific baud rate, // using interrupt or polling method. void init_com_port(int com_no, long baudrate, int intr = 0) { // calculate divisor relative to the baudrate short divisor = (long)115200 / baudrate; used_com_addr = get_com_addr(com_no); if (used_com_addr == 0) { printf("no valid com port!\n"); return ; } printf("serial com port addr 0x%x", used_com_addr); if (intr) printf(" [irq %d, ", used_com_irq); else printf("["); int uart_type = detect_uart_type(); switch(uart_type) { //case 0: break; // port must be found already by bios. case 1: printf("8250"); break; case 2: printf("16450"); break; case 3: printf("16550"); break; case 4: printf("16550a"); break; } printf("] is initialized!\n"); intr_used = intr; disable(); // turn off interrupts outportb(used_com_addr + 1, 0); // set dlab bit, so we can update the divisor outportb(used_com_addr + LCR, 0x80); // set divisor lsb outportb(used_com_addr + DIVLSB, divisor & 0xff); // set msb outportb(used_com_addr + DIVMSB, (divisor >> 8) & 0xff); // frame: 8 data bits, no parity and 1 stop bit outportb(used_com_addr + LCR, 0x3); // set RTS | DTR | OUT2(if intr) to inform remote system that we are ready outportb(used_com_addr + MCR, 0x3 | ((intr == 1) << 3)); // support interrupt? if (intr) { // save old serial port interrupt handler address old_handler = getvect(8 + used_com_irq); setvect(8 + used_com_irq, new_handler); // enable serial port irq at pic outportb(0x21, inportb(0x21) & ~(1 << used_com_irq)); // let the interrupt be triggered upon data arrival outportb(used_com_addr + IER, 1); } else { // no interrupt should be triggered outportb(used_com_addr + IER, 0); } // does the uart support fifo? if (uart_type == 4) { // set fifo buffer of 14 bytes, clear receive and transmit fifo's outportb(used_com_addr + FCR, 0xc7); } // clear delta bits inportb(used_com_addr + LSR); // clear incoming byte inportb(used_com_addr + RBR); enable(); } // the serial port interrupt handler // called upon received data only // saves data in a stack void interrupt new_handler(...) { unsigned char status = 0; disable(); // read iir and msr to acknowledge the uart inportb(used_com_addr + IIR); inportb(used_com_addr + MSR); // as long as data is arriving, put it in the stack do { // read status register status = inportb(used_com_addr + LSR) & 0x1; if (status & 1) { // read data from com port to the stack *next_char++ = inportb(used_com_addr + RBR); // overlap offset in case the stack is full next_char = ((next_char - recv_stack) % STACK_SIZE) + recv_stack; } }while (status & 1); enable(); // let the pic know we are done outportb(0xa0, 0x20); outportb(0x20, 0x20); } // send a byte to the initialized com port void send_byte(unsigned char ch) { // make sure the connection is alive if (inportb(used_com_addr + MSR) & 0x80 == 0) return; // make sure the transmit hold register is empty while (inportb(used_com_addr + LSR) & 0x20 == 0) ; // send the character outportb(used_com_addr + THR, ch); } // receive a byte from the initialized com port unsigned char recv_byte(int* is_read) { int i; *is_read = 0; if (intr_used) if (next_char > recv_stack) { *is_read = 1; return *--next_char; } else return 0; // is data set ready high? for (i = 5; i > 0; i--) if (inportb(used_com_addr + MSR) & 0x20) break; if (!i) return -1; // is there anything to read? for (i = 5; i > 0; i--) if (inportb(used_com_addr + LSR) & 0x1) break; if (!i) return -2; *is_read = 1; return inportb(used_com_addr + RBR); } // enter loop-mode for debugging and testing. void enter_loop_mode() { outportb(used_com_addr + MCR, inportb(used_com_addr + MCR) | 0x10); } // exit a loop mode, change to transimtter/receiver mode void exit_loop_mode() { outportb(used_com_addr + MCR, inportb(used_com_addr + MCR) & ~0x10); } // shut down serial port connection void shutdown_com_port() { disable(); if (intr_used) { // set the old handler setvect(8 + used_com_irq, old_handler); // disable used serial port interrupt at pic outportb(0x21, inportb(0x21) | (1 << used_com_irq)); } // disable serial port interrupt outportb(used_com_addr + IER, 0); // disable interrupt, RTS and DTR outportb(used_com_addr + MCR, 0); outportb(used_com_addr + FCR, 0); enable(); } int main() { clrscr(); init_com_port(1, 9600, 1); for(;;) { if (kbhit()) { int c = getch(); if (c == 27) break; printf("%c", c); send_byte(c); } int b = 0; unsigned char c = recv_byte(&b); if (b) printf("%c", c); } shutdown_com_port(); return 1; }