How can I communicate over USB using the LPC1788 microcontroller? - c

I'm working with the NXP LPC1788 microcontroller at the moment, and I'm trying to configure it to communicate with a Windows 7 PC using USB. I have limited experience with USB (I started learning the protocol at the start of this week), but I've worked with the LPC1788 for quite some time and have experience with other communication protocols (CAN, I2C, SSP).
I want to configure my microcontroller to act as a device and for the PC to act as the host. I'm suspecting that I will need to configure the microcontroller to communicate using full-speed interrupt transfers. Additionally, I will likely need to create my own vendor-specific USB driver for the PC later - I have not done this yet, and my descriptors are not properly configured.
My specific problem is that when I run my program and initialise/enable the microcontroller's USB device, I receive only two USB interrupts. The device interrupt status (DEVINTST) values in each case are:
0x19 - (FRAME, DEVSTAT, and CCEMPTY interrupts)
0x1 - (FRAME interrupt)
In the case of receiving the DEVSTAT interrupt, I read the following value from the serial interface engine using a GET DEVICE STATUS command:
0x19 - (CON (connected), SUS_CH (suspend state change), and RST (bus reset))
Using USBlyzer, I obtain only the following four packets: http://i.imgur.com/WRk7RBv.png.
I'm expecting that even without properly configured descriptors or a matching driver on the PC end, I should still be receiving more than this. I would have expected to be receiving a Get Descriptor request in Endpoint 0.
My main function only initialises my USB device and loops indefinitely.
The USB initialisation code is given below:
void USBInit()
{
// Turn on power and clock
CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);
// PLL0 clock is 96 MHz, usbclk should be 48 MHz.
LPC_SC->USBCLKSEL = 0x102;
// Configure USB pins.
PINSEL_ConfigPin(0, 29, 1); // USB_D+1
PINSEL_ConfigPin(0, 30, 1); // USB_D-1
PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
PINSEL_ConfigPin(2, 9, 1); // USB_CONNECT1
PINSEL_ConfigPin(1, 30, 2); // USB_VBUS
//PINSEL_ConfigPin(1, 19, 2); // USB_PPWR1
PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);
// Set DEV_CLK_EN and AHB_CLK_EN.
LPC_USB->USBClkCtrl |= 0x12;
// Wait until change is reflected in clock status register.
while((LPC_USB->USBClkSt & 0x12) != 0x12);
// Select USB port 1.
LPC_USB->USBClkCtrl |= 0x8;
while((LPC_USB->USBClkSt & 0x8) == 0);
LPC_USB->StCtrl &= ~0x3;
LPC_USB->USBClkCtrl &= ~0x8;
// Reset the USB.
USBReset();
// Configure interrupt mode.
writeSIECommandData(CMD_SET_MODE, 0);
// Enable NVIC USB interrupts.
NVIC_EnableIRQ(USB_IRQn);
// Set device address to 0x0 and enable device & connection.
USBSetAddress(0);
USBSetConnection(TRUE);
//printf("USB initialised\n");
//printf("EpIntEn: 0x%x\n", LPC_USB->EpIntEn);
// No errors here (SIE Error code: 0x0).
USBPrintErrCode();
// Packet sequence violation here (SIE Error code: 0x11).
USBPrintErrCode();
}
The USB reset function:
void USBReset()
{
LPC_USB->EpInd = 0;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
LPC_USB->EpInd = 1;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
while ((LPC_USB->DevIntSt & EP_RLZED_INT) == 0);
LPC_USB->EpIntClr = 0xFFFFFFFF;
LPC_USB->EpIntEn = 0xFFFFFFFF;
LPC_USB->DevIntClr = 0xFFFFFFFF;
LPC_USB->DevIntEn = DEV_STAT_INT | EP_SLOW_INT | EP_FAST_INT;
}
The USB set address function:
void USBSetAddress(uint32_t addr)
{
writeSIECommandData(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | addr));
writeSIECommandData(CMD_SET_ADDR, DAT_WR_BYTE(DEV_EN | addr));
}
The USB set connection function:
void USBSetConnection(uint32_t connect)
{
writeSIECommandData(CMD_SET_DEV_STAT, DAT_WR_BYTE(connect ? DEV_CON : 0));
}
The USB interrupt service routine:
void USB_IRQHandler(void)
{
uint32_t data;
uint32_t interruptData = LPC_USB->DevIntSt;
printf("InterruptData: 0x%x\n", interruptData);
// Handle device status interrupt (reset, connection change, suspend/resume).
if(interruptData & DEV_STAT_INT)
{
LPC_USB->DevIntClr = DEV_STAT_INT;
writeSIECommand(CMD_GET_DEV_STAT);
data = readSIECommandData(DAT_GET_DEV_STAT);
printf("Data: 0x%x\n", data);
// Device reset.
if(data & DEV_RST)
{
USBReset();
USBResetCore();
printf("USB Reset\n");
}
// Connection change.
if(data & DEV_CON_CH)
{
printf("Connection change\n");
/* Pass */
}
// Suspend/resume.
if(data & DEV_SUS_CH)
{
if(data & DEV_SUS)
{
printf("USB Suspend\n");
USBSuspend();
}
else
{
printf("USB Resume\n");
USBResume();
}
}
return;
}
// Handle endpoint interrupt.
if(interruptData & EP_SLOW_INT)
{
printf("Endpoint interrupt\n");
}
if(interruptData & EP_FAST_INT)
{
printf("Endpoint interrupt\n");
}
}
The terminal output:
InterruptData: 0x19
Data: 0x19
USB Reset
USB Resume
InterruptData: 0x1
Edit: Using the SIE's GET ERROR CODE command, I found that I end up with an "unexpected packet" error by the end of the USB initialisation function. However, if I read the test register, I get back 0xA50F as expected, meaning that my communication with the SIE is working and my USB/AHB clocks are presumably configured correctly and running.

I managed to get an example USB project working in IAR Embedded Workbench. Based on that, I made two changes that mostly fixed my program:
I changed the configuration of my project from "semihosted" to "SWO" which made my print outputs a lot faster.
I removed the following line. It apparently misconfigured the USB clock rate.
LPC_SC->USBCLKSEL = 0x102;

Related

MSP430 I2C reading a SDP610 differential pressure sensor issue

I am trying to read a SDP610 sensiron differential pressure sensor via a Texas Instruments msp430.
I am having the issue of the sensor not acknowledging the command and thus, not communicating the pressure value itself. Note I have confirmed that the sensor works by hooking it up to an arduino via an opensource library and, I can see the data via this. Note my IDE is code composer. My chips is MSP430FR2311 (a launch pad breakout board).
My hardware setup is 4 wires. Vcc(3.3V), Ground(0V), SDK and SCL. The SDK and SCL lines are pulled to VCC with a 4.7Kohm resistor as per specification.
I have the following code for my MSP430 see below:
However, I do not see the response of the sensor via a logic analyser. Here is my capture. You will have to click the link. Note the top line is clock and bottom is the data.
MSP430 output.
The logic flow for reading the sensor from the datasheet and from the arduino code is as follows:
Write address of the device to the I2C line(8 bit h81)
Wait for slave acknowledge
Write command for reading (8 bit hF1)
Wait for slave acknowledge
Slave holds the master
Slave outputs 3 bytes (2 data one msb and 1 lsb then a check sum)
acknowledge
This is the datasheet for the sensor
Any tips to why the sensor is not responding.
CODE
void Read_Diff_pressure(void)
{
int rx_byte;
UCB0CTL1 |= UCTXSTT+ UCTR; // Generating START + I2C transmit (write)
UCB0I2CSA = SDP610Address; // SDP610 7 bit address 0x40
UCB0TXBUF = SDP610Read; // sending the read command 0x78
while(!(UCB0IFG & UCTXIFG)); //wait until reg address got sent
while( UCB0CTL1 & UCTXSTT); //wait till START condition is cleared
UCB0CTL1 |= UCTXSTT; //generate RE-START
UCB0I2CSA = SDP610Address; // SDP610 7 bit address 0x40
UCB0CTL1 &=~ UCTR; //receive mode
while( UCB0CTL1 & UCTXSTT); //wait till START condition is cleared
rx_byte = UCB0RXBUF; //read byte
//while(!(UCB0IFG & UCRXIFG)); //wait while the Byte is being read
UCB0CTL1 |= UCTXNACK; //generate a NACK
UCB0CTL1 |= UCTXSTP; //generate stop condition
while(UCB0CTL1 & UCTXSTP); //wait till stop condition got sent```
Pressure_result = rx_byte;
}
void InitI2C_diff(void)
{
PAOUT |= I2C_SCL_PIN|I2C_SDA_PIN;//P1.2(SDA) - P1.3(SCL) as per silk screen defined in a header
PADIR |= I2C_SCL_PIN|I2C_SDA_PIN;
PASEL0 |= (I2C_SCL_PIN|I2C_SDA_PIN); // configure I2C pins (device specific)
UCB0CTLW0 |= UCSWRST; // put eUSCI_B in reset state
UCB0CTLW0 |= UCMODE_3 | UCSYNC | UCMST; // I2C master mode, SMCL
UCB0CTL1 = UCSSEL_2 + UCSWRST; //use SMCLK + still reset
UCB0BR0 = 10; // default SMCLK 1M/10 = 100KHz
UCB0BR1 = 0; //
UCB0I2CSA = SDP610Address; //The address of the device
UCB0CTLW0 &= ~UCSWRST; // eUSCI_B in operational state
//UCB0BRW = 64; // baudrate = SMCLK / 64
}
int main(void)
{
InitI2C_diff();//Init the i2c
while (1) { // Mainloop
Read_Diff_pressure();
delay(1000);//1 Second delay before re looping
}
}
A few parts were missing compared to an old Project implementation of mine (VCNL3020 + MSP430).
For example:
set the 7-bit addressing mode, single-master environment, I2C Master, synchronous mode,..Maybe I have overlooked it
Does the sensor need itself an init?
The Init Part of the I2C only looked like this:
void I2CInit( void )
{
P1SEL |= BIT6 + BIT7; // Assign I2C pins to USCI_B0
P1SEL2|= BIT6 + BIT7;
UCB0CTL1 |= UCSWRST; // Enable SW reset
UCB0CTL0 = UCMST + UCMODE_3 + UCSYNC; // 7-bit addressing, single-master environment, I2C Master, synchronous mode
UCB0CTL1 = UCSSEL_2 + UCSWRST; // Use SMCLK, keep SW reset
UCB0BR0 = 16; // fSCL = SMCLK/UCB0BR1
UCB0BR1 = 0;
UCB0I2CIE |= UCNACKIE; // Enable not-acknowledge interrupt
UCB0I2CSA=slave_adress;
UCB0CTL1 &= ~UCSWRST; // Clear SW reset, resume operation
IE2 |= UCB0TXIE + UCB0RXIE; // Enable TX&RX interrupts
}
To not make it unnecessary complicated, you could check my implementation on github and see if it helps Github Link I2C MSP430 Main
I hope this helps a bit- have fun!
I'm not sure what your hardware looks like, but your I2C pull-ups sound too large.I know of lot of app notes talk about about 4.7K, but I'd look at the rise time of the lines with an oscilloscope. If you don't have access to a scope, I'd use 1K or 2 K and see what happens.

Linux 4.5 GPIO Interrupt Through Devicetree on Xilinx Zynq Platform

I am using a custom development board with a Zynq XC72010 used to run a Linux 4.5 kernel. I am developing a device driver for a chip we are testing in house and I am having a lot of issues trying to bind a GPIO line to a software IRQ. So far I have tried a few methods and exhausted any google search I can think of. The relevant parts of my devicetree configuration:
/ {
compatible = "xlnx,zynq-7000";
amba {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
interrupt-parent = <&intc>;
ranges;
intc: interrupt-controller#f8f01000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0xF8F01000 0x1000>,
<0xF8F00100 0x100>;
};
i2c0: i2c#e0004000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <&clkc 38>;
interrupt-parent = <&intc>;
interrupts = <0 25 4>;
reg = <0xe0004000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
// I WANT INTERRUPT TO TRIGGER
// ON THIS DEVICE (axi_gpio_0, pin 2)
device: device#48 {
compatible = "device,name";
reg = <0x48>;
reset-gpios = <&axi_gpio_0 1 0>;
interrupt-parent = <&axi_gpio_0>;
interrupt-gpios = <&axi_gpio_0 2 0>;
};
};
};
amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_gpio_0: gpio#41200000 {
#gpio-cells = <2>;
compatible = "xlnx,xps-gpio-1.00.a";
gpio-controller;
interrupt-parent = <&intc>;
interrupts = <0 31 4>;
reg = <0x41200000 0x10000>;
xlnx,all-inputs = <0x0>;
xlnx,all-inputs-2 = <0x0>;
xlnx,all-outputs = <0x0>;
xlnx,all-outputs-2 = <0x0>;
xlnx,dout-default = <0x00000000>;
xlnx,dout-default-2 = <0x00000000>;
xlnx,gpio-width = <0x10>;
xlnx,gpio2-width = <0x20>;
xlnx,interrupt-present = <0x1>;
xlnx,is-dual = <0x0>;
xlnx,tri-default = <0xFFFFFFFF>;
xlnx,tri-default-2 = <0xFFFFFFFF>;
};
};
I am trying to assign an interrupt to pin 2 of 'axi_gpio_0' inside of 'device'.
Browsing google yielded 3 common methods for binding the interrupt in driver code:
/* Method 1 */
device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"interrupt", GPIOD_IN);
if(IS_ERR(device->interrupt_gpio))
return PTR_ERR(device->interrupt_gpio);
printk("device: Interrupt GPIO = %d\n",desc_to_gpio(device->interrupt_gpio));
irq = gpiod_to_irq(device->interrupt_gpio);
printk("device: IRQ = %d\n",irq);
ret = devm_request_threaded_irq(&i2c_client->dev, irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
/* Method 2 */
device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,
"interrupt", GPIOD_ASIS);
if (IS_ERR(device->interrupt_gpio))
return PTR_ERR(device->interrupt_gpio);
if (device->interrupt_gpio) {
dev_info(&i2c_client->dev, "Found interrupt GPIO: %d\n",desc_to_gpio(device->interrupt_gpio));
dev_info(&i2c_client->dev, "IRQ Number: %d\n",gpiod_to_irq(device->interrupt_gpio));
gpio_request(desc_to_gpio(device->interrupt_gpio), "DEVICE_INT"); // Request a GPIO pin from the driver
gpio_direction_input(desc_to_gpio(device->interrupt_gpio)); // Set GPIO as input
gpio_set_debounce(desc_to_gpio(device->interrupt_gpio), 50); // Set a 50ms debounce, adjust to your needs
gpio_export(desc_to_gpio(device->interrupt_gpio), false); // The GPIO will appear in /sys/class/gpio
ret = request_irq(gpiod_to_irq(device->interrupt_gpio), // requested interrupt
(irq_handler_t) irqHandler, // pointer to handler function
IRQF_TRIGGER_RISING, // interrupt mode flag
"DEVICE_IRQ_HANDLER", // used in /proc/interrupts
NULL); // the *dev_id shared interrupt lines, NULL is okay
if (ret != 0) {
dev_err(&i2c_client->dev,
"Failed to request IRQ: %d\n", ret);
}
}
else {
dev_err(&i2c_client->dev, "Failed to get interrupt GPIO pin\n");
}
/* Method 3 */
dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
I tried combinations of all of these methods and various devicetree configurations, but none of them achieved the functionality I need.
Method 1 results in this output:
device: Interrupt GPIO = 892
device: IRQ = -6
device 0-0048: Failed to request IRQ: -22
Method 2 results in this output:
device 0-0048: Found interrupt GPIO: 892
device 0-0048: IRQ Number: -6
device 0-0048: Failed to request IRQ: -22
So, trying to use the descriptor GPIO and the old GPIO api's are both unsuccessful in binding an interrupt.
To try method 3, I tweaked the devicetree:
device: device#48 {
compatible = "device,name";
reg = <0x48>;
interrupt-parent = <&axi_gpio_0>; // or <&intc>?
interrupts = <0 2 0x02>; // trying to grab pin 2
};
Method 3 results in this output:
genirq: Setting trigger mode 2 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: IRQ requested: 168
genirq: Setting trigger mode 8 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: Failed to request IRQ: -22
It seems the problem is assigning a software interrupt to a specific GPIO in Linux. I don't see what I'm missing here. Any advice is appreciated.
EDIT 1:
I found out that Linux doesn't like low-level interrupts for whatever reason. Changing method 3 to:
device: device#48 {
compatible = "device,name";
reg = <0x48>;
interrupt-parent = <&axi_gpio_0>;
interrupts = <0 2 0x04>;
};
And driver code to:
dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);
ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
allows me to successfully request an IRQ. However, my signal is active low, so this doesn't really help me much. Also, I'm not sure that this method is referencing axi_gpio_0 pin 2 as the interrupt signal. I can use both intc and axi_gpio_0 as interrupt-parent and it maps to the same IRQ number (I see this from cat /proc/interrupts). So, ignoring the polarity of the signal, how do I make sure that my registered interrupt is triggered based on the toggling of axi_gpio_0 pin 2?
EDIT 2:
I traced the issue with requesting an active-low interrupt to the driver for the interrupt controller: kernel/drivers/irqchip/irq-gic.c. This section of code is what causes the problem:
static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
/* Interrupt configuration for SGIs can't be changed */
if (gicirq < 16)
return -EINVAL;
/* SPIs have restrictions on the supported types */
if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
return gic_configure_irq(gicirq, type, base, NULL);
}
Hacking the kernel is not at all what I want to do, but commenting out:
/* SPIs have restrictions on the supported types */
/*if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;*/
allows me to request an active-low interrupt. For testing purposes, this should work temporarily.
UPDATE:
I have successfully created an IRQ on a GPIO pin. My problem was with the GPIO controller I was using. The controller was a Xilinx IP block inside of the Zynq Programmable Logic block and this controller is unable to trigger interrupts on GPIO pins (for reasons unknown to me). I soldered the interrupt pin on the board I was working on to a GPIO pin on a different, more generic controller and now Linux is playing nicely with me.
To summarize, a GPIO controller that matches compatible = "xlnx,xps-gpio-1.00.a"; is unable to bind to software interrupts in Linux. If you have this problem, USE A DIFFERENT GPIO CONTROLLER.
Thanks everyone for the help.
Using the device tree node of method 3, you should be able to retrieve the IRQ using irq_of_parse_and_map(i2c_client->dev.of_node, 0).
The retrieved IRQ can then be requested as you have done it with devm_request_threaded_irq().

C8051F340 USB device data transfer silicon lab IDE

I am trying to write a device code for C8051F340 to get the data from the host(PC) via USB. I have some example from silicon lab and the code look like below:
void Receive_File(void)
{
ReadStageLength = ((BytesToRead - BytesRead) > MAX_BLOCK_SIZE_READ)? MAX_BLOCK_SIZE_READ:(BytesToRead - BytesRead);
BytesRead += Block_Read((U8*)(&TempStorage[BlockIndex]), ReadStageLength); // Read Block
BlockIndex++;
// If device has received as many bytes as fit on one FLASH page, disable interrupts,
// write page to flash, reset packet index, enable interrupts
// Send handshake packet 0xFF to host after FLASH write
if ((BlockIndex == (BLOCKS_PR_PAGE)) || (BytesRead == BytesToRead))
{
Page_Erase((U8*)(PageIndices[PageIndex]));
Page_Write((U8*)(PageIndices[PageIndex]));
PageIndex++;
Led1 = !Led1;
BlockIndex = 0;
Buffer[0] = 0xFF;
Block_Write(Buffer, 1); // Send handshake Acknowledge to host
}
// Go to Idle state if last packet has been received
if (BytesRead == BytesToRead) {M_State = ST_IDLE_DEV; Led1 = 0;}
}
// Startup code for SDCC to disablt WDT before initializing variables so that
// a reset does not occur
#if defined SDCC
void _sdcc_external_startup (void)
{
PCA0MD &= ~0x40; // Disable Watchdog timer
}
#endif
I have some questions want to ask:
1. Where the data goes? the Buffer [0]?
2. if I got a Hex value transfer from the host, can I just read the Buffer [0] to get it ?
sorry I am a newbie.
Thank you.
Your received data stored in array TempStorage
You used Buufer[0] (the value 0xFF) for to send data to host

pic32 start bit does not clear -- Basic I2C setup

I am new to embedded programming and am trying to get my first I2C project working. I am using the PIC32MX795F512L. I am pretty much following the microchip datasheet for I2C on PIC32. The problem I'm having is the S bit is never cleared from the I2C1STAT register. It is actually unclear to me whether I have to do this or if it is done automatically at the conclusion of the Start event, but right now I'm trying to manually clear it. However, nothing that I do seems to have an effect. If more information is needed to make it easier to understand what is happening let me know. I am using a PICKIT3 so I can get debugging information as well. I know that the Master interrupt occurs, the S bit gets set, I exit the interrupt code and hang on the while statement checking the I2C1STATbits.S.
Edit: I'm editing this post to have my new code instead of the old code. I am now using a 20MHZ peripheral clock. Just one of the many things I tried today that did not work. Delay is just a 256ms delay. Super long I know, but it was quick.
main()
{
//Setup I2C1CON
I2C1CONbits.SIDL = 0; //Continue to run while in Idle
I2C1CONbits.SCLREL = 1; //Release the clock (Unsure of this)
I2C1CONbits.A10M = 0; //Using a 7 bit slave address
I2C1CONbits.DISSLW = 1; //Slew rate control disabled because running at 100 KHZ
I2C1ADD = 0x1E; //Slave address without read or write bit
I2C1BRG = 0x060; //Set the BRG clock rate - Based on Page 24-19
I2C1CONbits.ON = 1; //Turn on the I2C module
delay();
I2C1CONbits.SEN = 1; //Initiate a start event
while(I2C1CONbits.SEN == 1); //Wait until Start event is done
I2C1TRN = 0x3C; //Load the address into the Transmit register
while(I2C1STATbits.TRSTAT == 1);
while(I2C1STATbits.ACKSTAT == 0); //Wait for a ACK from the device
I2C1TRN = 0x00;
while(I2C1STATbits.TRSTAT == 1);
while(I2C1STATbits.ACKSTAT == 0);
I2C1TRN = 0x70;
while(I2C1STATbits.TRSTAT == 1);
while(I2C1STATbits.ACKSTAT == 0);
while(1);
}
Thanks for any help.
I'm also just beggining on PIC32MZ family, setting up the I2C to talk to various memory chips.
I used your code and modified it so that it would work properly. Since I am using PIC32MZ family, I believe the I2C registers should probably be the same.
I2C configuration:
I2C1CONbits.SIDL = 0; // Stop in Idle Mode bit -> Continue module operation when the device enters Idle mode
I2C1CONbits.A10M = 0; // 10-bit Slave Address Flag bit -> I2CxADD register is a 7-bit slave address
I2C1CONbits.DISSLW = 1; // Slew Rate Control Disable bit -> Slew rate control disabled for Standard Speed mode (100 kHz)
I2C1CONbits.ACKDT = 0; // Acknowledge Data bit -> ~ACK is sent
I2C1BRG = 0x0F3; // Baud Rate Generator set to provide 100KHz for SCL with 50 MHz xtal.
I followed the transmission steps provided in the I2C Datasheet so it would be easy to follow the steps coupled with the pdf and my comments on the code.
I2C Data Transmission:
// 1. Turn on the I2C module by setting the ON bit (I2CxCON<15>) to ‘1’.
I2C1CONbits.ON = 1; // I2C Enable bit -> Enables the I2C module and configures the SDAx and SCLx pins as serial port pins
//------------- WRITE begins here ------------
// 2. Assert a Start condition on SDAx and SCLx.
I2C1CONbits.PEN = 0; // Stop Condition Enable Bit -> Stop Condition Idle
I2C1CONbits.SEN = 1; // Start Condition Enable bit -> Initiate Start condition on SDAx and SCLx pins; cleared by module
while(I2C1CONbits.SEN == 1); // SEN is to be cleared when I2C Start procedure has been completed
// 3. Load the Data on the bus
I2C1TRN = 0b10100000 ; // Write the slave address to the transmit register for I2C WRITE
while(I2C1STATbits.TRSTAT == 1); // MASTER Transmit still in progress
while(I2C1STATbits.ACKSTAT == 1); // Master should receive the ACK from Slave, which will clear the I2C1STAT<ACKSTAT> bit.
I2C1TRN = 0xCE; // Register Address
while(I2C1STATbits.TRSTAT == 1);
while(I2C1STATbits.ACKSTAT == 1);
I2C1TRN = 0xCF; // Register Value
while(I2C1STATbits.TRSTAT == 1);
while(I2C1STATbits.ACKSTAT == 1);
I2C1CONbits.PEN = 1; // Stop Condition Enable Bit -> Initiate Stop condition on SDAx and SCLx pins; cleared by module
//-------------- WRITE ends here -------------
This code works well as I used a couple of LEDs to toggle indicating a successful write procedure.
The while(!(I2C1STATbits.ACKSTAT == 0)); is the right way i guess.I2C1ADD is used to set the slave address of the current PIC mc if you are using your I2C as slave.If any master requests start with that slave address the PIC will respond to that as slave.
Read this pdf where initially they have specified that in PIC the I2C is configured as both master and slave and the master request is checked by the same PIC with with the slave address in the I2C1ADD value.
http://ww1.microchip.com/downloads/en/DeviceDoc/61116F.pdf

Receiving SPI data via DMA on PIC32

I know that this topic (DMA & SPI) has already been talked about on numerous threads in the microchip forum, actually i've read all the 15 pages in result of the search with keyword "dma" and read all the topics about dma & spi.
And I am still stuck with my problem I hope someone can help me :)
Here is the problem.
My chip is a PIC32MX775F512H.
I am trying to receive (only receive) data using SPI via DMA.
Since you cannot "just" receive in SPI, and that the SPI core starts toggling the SPI clock only if you write into the SPIBUF (SPI1ABUF for me) I am trying to receive my data using 2 DMA channels.
DMA_CHANNEL1 for the transmitting part.
DMA_CHANNEL2 for the receiving part.
I copy pasted the code from http://www.microchip.com/forums/tm.aspx?tree=true&high=&m=562453&mpage=1#
And tried to make it work without any luck. It only receives several bytes (5 or 6).
I've set the Event Enable Flags to DMA_EV_BLOCK_DONE for both dma channels, no interrupt occurs.
Do you have any idea ?
Here is the code I am using :
int Spi_recv_via_DMA(SPI_simple_master_class* SPI_Port, int8u *in_bytes, int16u num_bytes2)
{
DmaChannel dmaTxChn=DMA_CHANNEL1;
DmaChannel dmaRxChn=DMA_CHANNEL2;
SpiChannel spiTxChn=SPI_Port->channel;
int8u dummy_input;
DmaChnOpen(dmaTxChn, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);
DmaChnOpen(dmaRxChn, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);
DmaChnSetEventControl(dmaTxChn, DMA_EV_START_IRQ_EN | DMA_EV_START_IRQ(_SPI1A_RX_IRQ));
DmaChnSetEventControl(dmaRxChn, DMA_EV_START_IRQ_EN | DMA_EV_START_IRQ(_SPI1A_RX_IRQ));
DmaChnClrEvFlags(dmaTxChn, DMA_EV_ALL_EVNTS);
DmaChnClrEvFlags(dmaRxChn, DMA_EV_ALL_EVNTS);
DmaChnSetEvEnableFlags(dmaRxChn, DMA_EV_BLOCK_DONE);
DmaChnSetEvEnableFlags(dmaTxChn, DMA_EV_BLOCK_DONE);
//SpiChnClrTxIntFlag(spiTxChn);
//SpiChnClrRxIntFlag(spiTxChn);
DmaChnSetTxfer(dmaTxChn, tx_dummy_buffer, (void *)&SPI1ABUF, num_bytes2, 1, 1);
DmaChnSetTxfer(dmaRxChn, (void *)&SPI1ABUF, in_bytes, 1, num_bytes2, 1);
while ( (SPI1ASTAT & SPIRBE) == 0)
dummy_input = SPI1ABUF;
SPI1ASTAT &= ~SPIROV;
DmaRxIntFlag = 0;
DmaChnEnable(dmaRxChn);
DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0);
while(!DmaRxIntFlag);
return 1;
}
with those two interrupt handlers :
// handler for the DMA channel 1 interrupt
void __ISR(_DMA1_VECTOR, ipl5) DmaHandler1(void)
{
int evFlags; // event flags when getting the interrupt
//LED_On(LED_CFG);
INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL1)); // acknowledge the INT controller, we're servicing int
evFlags=DmaChnGetEvFlags(DMA_CHANNEL1); // get the event flags
if(evFlags&DMA_EV_BLOCK_DONE)
{ // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
DmaTxIntFlag = 1;
DmaChnClrEvFlags(DMA_CHANNEL1, DMA_EV_BLOCK_DONE);
}
// LED_Off(LED_CFG);
}
void __ISR(_DMA2_VECTOR, ipl5) DmaHandler2(void)
{
int evFlags; // event flags when getting the interrupt
INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL2)); // acknowledge the INT controller, we're servicing int
evFlags=DmaChnGetEvFlags(DMA_CHANNEL2); // get the event flags
if(evFlags&DMA_EV_BLOCK_DONE)
{ // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
DmaRxIntFlag = 1;
DmaChnClrEvFlags(DMA_CHANNEL2, DMA_EV_BLOCK_DONE);
}
}
So I end up waiting forever at the line : while(!DmaRxIntFlag);
I have put breakpoints in the interrupt vectors, they are never called.
This is the state of several registers during the ever lasting wait :
DMACON 0x0000C800
DMASTAT 0x00000001
I am using SPI1A port, so SPI1ABUF and _SPI1A_RX_IRQ
DCH1SPTR 0x5
DCH1SSIZ 0x2B
DCH2DPTR 0x6
DCH2DSIZ 0x2B
DCH2CON 0x00008083
DCH2ECON 0x1B10
DCH2INT 0x00800C4
DCH2SSA 0x1F805820
DCH2DSA 0x00000620
Channel 1 is used to transmit
Channel 2 is used to receive
You are missing these:
INTEnable(INT_SOURCE_DMA(dmaTxChn), INT_ENABLED); // Tx
INTEnable(INT_SOURCE_DMA(dmaRxChn), INT_ENABLED); // Rx
rigth before
DmaRxIntFlag = 0;
DmaChnEnable(dmaRxChn);
DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0);
Good luck!
Are you using the SPI in slave mode? or you are on master mode, trying to read some response for a command?
Have you check the silicon errata for this chip? The dspic 33fj family had an issue where SPI slave mode simply didn't work.
Other than that, I don't think it is a good idea to busy wait for DmaRxIntFlag change. You should configure the DMA transfer and continue with your main loop. The DMA will trigger the interrupt handler.
Hope this helps.

Resources