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.
Related
I am trying to write a program that will enable me to send data from my TI microcontroller to the very common HD4478 LCD. Rather than utilizing a parallel pin setup, I thought it would be a good idea to try and use a serial setup, so the LCD has a PCF8574T I2C I/O expander backpack. I am fairly new to writing embedded programs, and this is my first time using any real serial wiring communication protocols (I2C/SPI) so I'm struggling a bit to get this to work. Before I explain my confusion, here are the datasheets for the 3 respective components:
Microcontroller Data Sheet: https://www.ti.com/lit/ds/symlink/tm4c123gh6pm.pdf
LCD data sheet: https://circuitdigest.com/sites/default/files/HD44...
I/O Expander Data Sheet: https://www.nxp.com/docs/en/data-sheet/PCF8574_PCF...
My main problem is, I have no real idea what I'm doing wrong, but I am assuming it's something with the way I am initializing the LCD to 4-bit mode. I am a bit confused by this initialization walkthrough on the LCD data sheet:
And then This latter explanation that further explains how to initialize it to 4 bit mode, which is what I'll be using with my I/O expander:
I don't think I quite grasp whether to send the initial function set Command to 4-bit mode in 8-bit mode or 4-bit mode, and when the exact changeover to 4-bit mode occurs in the initialization process.
The way I transfer data over the data line is the 4-bit mode, so I am sending the upper nibble. and then lower nibble. This goes for both data bytes and command bytes. Below is my code. Currently flashing it and running simply has no effect on the LCD. I know that I am operating at the correct slave address since I do not get any flags present after the initial slave address transmit that would indicate any error. However, all of my commands in I2C_LCD_Enable and the latter command in main() that attempts to send a singular character to the LCD have no effect as well.
#include "TM4C123.h" // Device header
#include "RTE_Components.h" // Component selection
//data pin should be open drain in i2c modules
#define DATA 1
#define COMMAND 0
//I can think of each Pin connected to command/control as a singular bit
void GPIOE_enable(void);
void I2C_enable(void);
void I2C_LCD_enable(void);
void I2C_transfer_byte(char byte,int mode);
void delay_50_ms(void);
uint32_t address_transfer_value;
uint32_t data_value;
#define E 0x04 //bit 2
int main(){
GPIOE_enable();
I2C_enable();
I2C_LCD_enable();
I2C_transfer_byte(0x01,COMMAND);
I2C_transfer_byte(0x80,COMMAND); //set cursor to first row
delay_50_ms();
I2C_transfer_byte('a',DATA);
}
//port E, pins pe4 and pe5, have the alternative function as acting the clock/data lines for I2C module 2
void GPIOE_enable(void){
SYSCTL->RCGCGPIO |= 0x10; //enable port E
GPIOE->DIR |= 0x10 | 0x20;
GPIOE->DEN |= 0x10 | 0x20; //enable pins pe4 and pe5
GPIOE->AFSEL = 0x10 | 0x20; //enable pins Pe4 and Pe5 for their alternate function
GPIOE->ODR |= 0x20; //pe5 is data pin, must be set to open drain
GPIOE->PCTL |= (3 << 16) | (3 << 20);
}
void I2C_enable(void){
SYSCTL->RCGCI2C |= 0x04;
I2C2->MCR |= 0x10; //initialize I2C master
GPIOE->PUR |= 0x10 | 0x20; //I pulled up the Clock and Data Lines because they were low: Not pulling them up won't allow them to transfer from their high to low states when transmission begins and the I2C->MCS & 0x01 condition hangs forever
I2C2->MTPR = 0x09; //see data sheet: This initializes SCL speed to 100k kbps
I2C2->MSA = (0x27 << 1); //see data sheet: This sets slave address and sets mode to TRANSMIT
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
}
//HD775 data sheet explains the initialization process on pages 24 and 42
void I2C_LCD_enable(void){
//not sure how to initialize quite yet...
/*I2C2->MDR = 0x28; //this initializes 4-bit mode. This command, AND THIS COMMAND ONLY, takes only one write since it's still in 8-bit mode
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = (1 << E); //set ENABLE to high
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = ~(1<<E); //set ENABLE to low
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();*/
I2C_transfer_byte(0x28,COMMAND);
I2C_transfer_byte(0x06,COMMAND); //Move cursor right
I2C_transfer_byte(0x01,COMMAND); //clear screen
I2C_transfer_byte(0x0F,COMMAND); //turn display on
}
//the upper 4 bits are the data pins : D4, D5, D6, D7 (bits 0-3)
//the lower 3 bits are: RS, RW, E
//to send a command, we should set RS to 0 to select "Command Mode" on LCD
//if mode is 0, or COMMAND, do a logical OR with 0, which will set RS, bit 0, to 0
//if mode is 1, or DATA, do a logical or with 1, so RS, bit 0, is set to 1
//we also need to pulse E, or enable to make sure any of these data/commands actually are executed
//The E pin corresponds to bit 2, so I'll send each data byte with first E enabled, and then E set to to low, to pulse Enable
//send upper nibble, pulse enable, send lower nibble, pulse enable
void I2C_transfer_byte(char byte,int mode){
char byte_shifted;
char byte_upper_nibble;
char byte_lower_nibble;
byte_shifted = byte << 4;
byte_upper_nibble = byte & 0xF0;
I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) | E; //set command to be most significant bit, and enable E
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01){
data_value = I2C2->MBMON;
}
delay_50_ms();
I2C2->MDR = (mode | (I2C2->MDR & 0xF0)| byte_upper_nibble) & ~E; //set command to be most significant bit, and disable E (pulsing E enables the command/data)
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
byte_lower_nibble = byte_shifted & 0xF0;
I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) | E;
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
I2C2->MDR = (mode | (I2C2->MDR & 0x0F) | byte_lower_nibble) & ~E;
I2C2->MCS |= 0x07;
while (I2C2->MCS & 0x01);
delay_50_ms();
}
//clock frequency is 1,000,000 cycles/second
void delay_50_ms(void){
SYSCTL->RCGCTIMER |= 0x01;
TIMER0->CTL = 0;
TIMER0->CFG |= 0x04;
TIMER0->TAMR |= 0x01 | 0x10; //single shot mode, enable interrupt
TIMER0->TAILR = 20000; //1,000,000 / 20,0000 = 50
TIMER0->CTL = 0x01;
while ((TIMER0->RIS & 0x01) == 0);
}
I am using the on-board 3.3V power supply from the TI board as my VCC supply.
I think before discussing that the LCD is not working properly, I want to know whether I2C is working properly and whether the Address PIN define.
The address pin of I/O Expander IC is all connected to HIGH, so your Slave address is 0x27
(I just want to make sure whether this part is okay because if there is a problem, there will be problems in the subsequent tests)
If I2C communication work normally, the I/O Expander IC control should be correct.
( Send some commands to see if the IC outputs according to your commands)
If the I/O expander IC work abnormally, maybe you can check the I2C signal use a logic analyzer or oscilloscope to check whether the I2C signal of the MCU is correct. (Slave Address, ACK...etc)
If the above of them is correct, we can start to check the LCD part!
Reminder: Your hyperlink is failed for the LCD and I/O Expander IC Data Sheet.
For the part of Initializing by internal reset circuit
It means that when you supply power to the LCD, it will perform the actions 1~4 below.
During this period, you can use an oscilloscope or logic analyzer to measure the BF pin. It should be HIGH, always When your VCC rises to 4.5V, BF will continue for another 10ms before pulling LOW.
But if VCC does not rise to 4.5V, it must be initialized through MPU.
I saw at the end of the article
I am using the on-board 3.3V power supply from the TI board as my VCC
supply.
Does your LCD use 3.3V as the power supply? If yes, you shall initialize LCD by MPU.
(But I suggest using the 5V to supply that can reduce the debug time.)
If using the internal initialize function, the LCD setting shall be like below:
8-bit interface
1-line display
5x8 dot character font
So if you want to change to 4-bit mode, need to use the Function set Instruction to set again. (Datasheet P.28)
The signal transmission part must be tested according to the timing diagram
It takes 4 steps to send a command
Transfer upper 4-bit data.
Busy flag check (BF=1)
Busy flag check (BF=0)
Transfer lower 4-bit data
Use the Example of the HD44780U Datasheet for testing (Datasheet P.42)
Step 6, need to be divided into 2 times to send.
And it will show the 'H' on the LCD.
I am trying to query a temperature sensor (TMP007) over an I2C bus using the L011K4's DMA controller. I've managed to get TMP007 to respond with good data, but that data isn't appearing in the buffer array that I pass my I2C DMA receive function.
What I expect to happen is that the I2C hardware is set up by the software in the Init and Read function, then the start condition is generated, the slave's address and register address are sent, then a second start/a restart is sent out followed by the slave's address again, and finally ending with a transmission of two bytes from TMP007 and a stop condition.
The data acquired from the temperature sensor should be appearing in a buffer that is passed into Start_Read (as MemoryBaseAddress), but that data does not appear when viewing the processor in debug mode after the read function has been called.
The Channel 3 transfer complete interrupt is being called, so I know the DMA has activated and managed the RX transfer, but the data doesn't appear in the desired buffer.
Here's an image of the communication being observed by a logic analyzer: https://i.imgur.com/SzySsXl.png
DIO2 is connected to the onboard LED, which tells us that the DMA transfer has completed (it's the only ISR with the LED toggle).
Any help would be greatly appreciated!
STM32L011K4 Datasheet: https://www.st.com/content/ccc/resource/technical/document/datasheet/42/c0/ab/e5/71/7a/47/0b/DM00206508.pdf/files/DM00206508.pdf/jcr:content/translations/en.DM00206508.pdf
STM32L011K4 Reference Manual: https://www.st.com/resource/en/reference_manual/dm00108282-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
void I2C1_DMA_Start_Read(int SlaveAddress, int RegisterAddress, int* MemoryBaseAddress, int BufferSize) {
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
DMA1_Channel3->CCR &= ~(0x00007FFF); // Channel 3 DMA mask
// Configure DMA Channel 3 for 16 bit memory and peripheral, and other aliased settings (reference manual page 249, 10.4.3)
DMA1_Channel3->CCR |= (0b11 << 12)|(0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;
DMA1_Channel3->CPAR = (uint32_t) &(I2C1->RXDR);
DMA1_Channel3->CMAR = (uint32_t) MemoryBaseAddress;
DMA1_Channel3->CNDTR = (uint16_t) BufferSize;
DMA1_Channel3->CCR |= DMA_CCR_EN; // Activate DMA channel 3
I2C1->CR1 |= I2C_CR1_NACKIE; // Enable no acknowledge interrupt for I2C1
I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address
I2C1->CR2 &= ~I2C_CR2_RD_WRN; // Master requests a write transfer in order to send the register
I2C1->CR1 |= I2C_CR1_PE|I2C_CR1_RXDMAEN; // Enable I2C1 and RX DMA for I2C1
I2C1->CR2 &= ~(0xFF << I2C_CR2_NBYTES_Pos); // Mask byte count
I2C1->CR2 |= (0x01 << I2C_CR2_NBYTES_Pos); // Transfer one byte (the slave's register address)
while(!(I2C1->ISR & I2C_ISR_TXE)); // Wait until TX data register is empty
I2C1->TXDR = RegisterAddress; // Set the transmit data to the desired register address
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
while(!(I2C1->ISR & I2C_ISR_TC)); // Wait until hardware clears the transmit complete bit
I2C1->CR2 |= I2C_CR2_RD_WRN; // Master requests a read transfer after sending over the register address
I2C1->CR2 &= ~(0xFF << I2C_CR2_NBYTES_Pos); // Mask byte count
I2C1->CR2 |= (0x02 << I2C_CR2_NBYTES_Pos); // Master looks for two bytes from the slave
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
return;
}
void DMA1_Channel2_3_IRQHandler() {
if(DMA1->ISR & DMA_ISR_TCIF3) {
GPIOB->ODR ^= 1 << 3; // Toggle the onboard LED as an indicator
I2C1->CR2 |= I2C_CR2_STOP; // Generate stop condition
while(I2C1->CR2 & I2C_CR2_STOP); // Wait until hardware clears the stop bit
DMA1->IFCR |= DMA_IFCR_CTCIF3; // Clear transfer complete interrupt bit
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
} else if(DMA1->ISR & DMA_ISR_TEIF3) {
I2C1->CR2 |= I2C_CR2_STOP; // Generate stop condition (if I2C comm is already stopped on error, delete this and following line)
while(I2C1->CR2 & I2C_CR2_STOP); // Wait until hardware clears the stop bit
DMA1->IFCR |= DMA_IFCR_CTEIF3; // Clear transfer error interrupt bit
I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
}
return;
}
void I2C1_IRQHandler() {
if(I2C1->ISR & I2C_ISR_NACKF) {
//GPIOB->ODR ^= 1 << 3; // Toggle the onboard LED as an indicator
I2C1->ICR |= I2C_ICR_NACKCF;
}
return;
}
I'm attempting to use the STM32L011K4's DMA controller to communicate with slave devices over I2C. Currently, I have no slave devices and am just trying to get the microcontroller to send the start condition out onto the I2C bus, but that is not happening.
When I run this code in debugging mode through the STM32CubeIDE, I notice that the start bit is set, but it never clears even though the reference manual says it should be cleared by hardware once the start condition occurs (page 656 for I2C_CR2).
Monitoring the SDA and SCL lines on my oscilloscope also show that they are a logical 1. Note: I'm using the NUCLEO-L011K4 on a breadboard, so the IO pins are tied to Vref through 1k resistors. All configuration registers appear to contain the desired value when the code is stuck sending the start condition, so I don't believe they are getting clobbered by a random line of code.
I'm not sure what's preventing the start condition from being sent, so any help would be greatly appreciated.
STM32L011K4 Datasheet:
https://www.st.com/content/ccc/resource/technical/document/datasheet/42/c0/ab/e5/71/7a/47/0b/DM00206508.pdf/files/DM00206508.pdf/jcr:content/translations/en.DM00206508.pdf
STM32L011K4 Reference Manual: https://www.st.com/resource/en/reference_manual/dm00108282-ultralowpower-stm32l0x1-advanced-armbased-32bit-mcus-stmicroelectronics.pdf
Initialization code:
void Init_I2C1_DMA() {
/* Basic I2C Initialization for 100 kHz I2C, 24 MHz SYSCLK, /1 APB1 scaler */
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // Enable peripheral clock for I2C1
I2C1->CR1 &= ~(I2C_CR1_PE); // Disable I2C1
I2C1->CR1 = 0; // Reset CR1
I2C1->TIMINGR = 0; // Reset timer settings
/* APB1 clock (I2C1 clock) is set in RCC_CFGR reg -- keep at divide by 1 -- 24 MHz SYSCLK
* Refer to table 103 for timing value source. t_presc was found to be 250 ns for 100 kHz I2C, so PRESC was set to match that for SYSCLK = 24 MHz
* All subsequent settings are copied from table 103 from STM32L011K4 reference manual
*/
I2C1->TIMINGR |= (0x5 << 28)|(0x4 << 20)|(0x2 << 16)|(0x0F << 8)|(0x13 << 0);
/* Desired settings:
* RXDMAEN enable, ANF enable.
*/
I2C1->CR1 |= (0x8 << I2C_CR1_DNF_Pos)|I2C_CR1_ERRIE;
I2C1->CR2 = 0; // Reset contents (ACKs are enabled by default)
NVIC_EnableIRQ(I2C1_IRQn);
NVIC_SetPriority(I2C1_IRQn, 0);
/* DMA initialization */
/* Since this is peripheral to memory, we use I2C1_RX, which is available on DMA channels 3,7. We used channel 3, but 7 would work the same. */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; // Enable peripheral clock for DMA1
DMA1_Channel3->CCR &= ~(0x00000001); // Disable Channel 3 DMA
// Configure DMA channel mapping
DMA1_CSELR->CSELR &= ~0x00000F00; // Channel 3 re-mapping mask
DMA1_CSELR->CSELR |= 0x00000600; // Channel 3 re-mapped to I2C1_RX
/* Configure NVIC for DMA */
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 0);
return;
}
void I2C1_DMA_Start_Read(uint8_t SlaveAddress, uint8_t RegisterAddress, int* MemoryBaseAddress, int BufferSize) {
// We need to put the device address out on the serial line before we can hand it over to the DMA
I2C1->CR1 &= ~(I2C_CR1_PE); // Disable I2C1
DMA1_Channel3->CCR &= ~DMA_CCR_EN; // Disable Channel 3 DMA
DMA1_Channel3->CCR &= ~(0x00007FFF); // Channel 3 DMA mask
// Configure DMA Channel 3 for 16 bit memory and peripheral, and other aliased settings (reference manual page 249, 10.4.3)
DMA1_Channel3->CCR |= (0b01 << 10)|(0b01 << 8)|DMA_CCR_MINC|DMA_CCR_TEIE|DMA_CCR_TCIE;
DMA1_Channel3->CPAR = (uint32_t) RegisterAddress;
DMA1_Channel3->CMAR = (uint32_t) MemoryBaseAddress;
DMA1_Channel3->CNDTR = (uint16_t) BufferSize;
I2C1->CR1 &= (~I2C_CR1_TXDMAEN); // Disable TX DMA for I2C1
I2C1->CR1 |= I2C_CR1_RXDMAEN; // Enable RX DMA for I2C1
// I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address for DMA read
while(!(I2C1->ISR & I2C_ISR_TXE));
I2C1->TXDR |= ((uint8_t) (SlaveAddress << 1)); // Set up the slave address for DMA read
I2C1->CR2 |= I2C_CR2_RD_WRN;
DMA1_Channel3->CCR |= DMA_CCR_EN; // Activate DMA channel 3
I2C1->CR1 |= I2C_CR1_PE; // Enable I2C1
I2C1->CR2 |= I2C_CR2_START; // Generate start condition
while(I2C1->CR2 & I2C_CR2_START); // Wait until hardware clears the start bit
// ???
return;
}
According with reference manual(p. 604)
you need uncomment I2C1->CR2 |= ((uint8_t) (SlaveAddress << 1)); and comment I2C1->TXDR |= ((uint8_t) (SlaveAddress << 1)); for set slave address, and you need set the number of bytes to be transferred.
I can't see your initialisation of GPIO. Check is GPIO settings right (Alternative function and open-drain mode).
Also in reference manual written this
PE must be kept low during at least 3 APB clock cycles in order to perform the software
reset. This is ensured by writing the following software sequence: - Write PE=0 - Check
PE=0 - Write PE=1.
I think you should try to do so.
Also I advise using 4.7k resistor for pulling to VDD.
I have a TIVA tm4c123G I have been trying to create a communication between it and my ADXL345 sensor using I2C protocol which I succeeded in writing and reading from the accelerometer the readings of the device address and the register values that I just wrote to which means everything is running fine. However I have tried this in step by step debugging in keil and it works fine but if I run the program it will give zeroes all the way and I have no idea why? Should I add delays between the write and read from registers or whats going wrong in my code?
Here is my code attached
I am using a clock of 80 MHZ for the system and I think this might be the problem however as the code goes too fast to the execution of a next send and there should be some delay? I am not sure I'm only guessing please help thanks !
also my connection for the adxl is
Vcc -> 3.3 volts
GND -> ground
CS -> 3.3 volts
SDO -> ground
SDA -> PB3
SCL -> PB2
#include "tm4c123gh6pm.h"
#include "stdint.h"
void EnableI2CModule0(void);
uint8_t ReadRegister(uint8_t RegisterAddress);
void PLL_Init(void);
void WriteRegister(uint8_t RegisterAddress,uint8_t Data);
volatile uint8_t X_Axis1,X_Axis2,Y_Axis1,Y_Axis2,Z_Axis1,Z_Axis2=0;
int main()
{
volatile long temp;
PLL_Init();
EnableI2CModule0();
temp=ReadRegister(0x00);
WriteRegister(0x2D,0x08);
temp=ReadRegister(0x2D);
WriteRegister(0x31,0x0B);
temp=ReadRegister(0x31);
while(1)
{
X_Axis1=ReadRegister(0x32);
X_Axis2=ReadRegister(0x33);
Y_Axis1=ReadRegister(0x34);
Y_Axis2=ReadRegister(0x35);
Z_Axis1=ReadRegister(0x36);
Z_Axis2=ReadRegister(0x37);
}
}
void PLL_Init(void){
// 0) Use RCC2
SYSCTL_RCC2_R |= 0x80000000; // USERCC2
// 1) bypass PLL while initializing
SYSCTL_RCC2_R |= 0x00000800; // BYPASS2, PLL bypass
// 2) select the crystal value and oscillator source
SYSCTL_RCC_R = (SYSCTL_RCC_R &~0x000007C0) // clear XTAL field, bits 10-6
+ 0x00000540; // 10101, configure for 16 MHz crystal
SYSCTL_RCC2_R &= ~0x00000070; // configure for main oscillator source
// 3) activate PLL by clearing PWRDN
SYSCTL_RCC2_R &= ~0x00002000;
// 4) set the desired system divider
SYSCTL_RCC2_R |= 0x40000000; // use 400 MHz PLL
SYSCTL_RCC2_R = (SYSCTL_RCC2_R&~ 0x1FC00000) // clear system clock divider
+ (4<<22); // configure for 80 MHz clock
// 5) wait for the PLL to lock by polling PLLLRIS
while((SYSCTL_RIS_R&0x00000040)==0){}; // wait for PLLRIS bit
// 6) enable use of PLL by clearing BYPASS
SYSCTL_RCC2_R &= ~0x00000800;
}
void EnableI2CModule0(void)
{
volatile int Delay=0;
SYSCTL_RCGCI2C_R|=0x00000001; //set i2c module 0 clock active
Delay=SYSCTL_RCGCI2C_R; //delay allow clock to stabilize
SYSCTL_RCGCGPIO_R |=0x00000002; //i2c module 0 is portB so activate clock for port B
Delay = SYSCTL_RCGCGPIO_R; //delay allow clock to stabilize
GPIO_PORTB_AFSEL_R|= 0x0000000C; //enable alternate functions for PB2 and PB3
GPIO_PORTB_ODR_R |= 0x00000008; //set PB3 (I2C SDA) for open drain
GPIO_PORTB_DEN_R |= 0xFF; //Enable digital on Port B
GPIO_PORTB_PCTL_R |=0x03;
I2C0_PP_R |= 0x01;
I2C0_MTPR_R |= 0x00000027; //set SCL clock
I2C0_MCR_R |= 0x00000010; //intialize mcr rigester with that value given in datasheet
}
uint8_t ReadRegister(uint8_t RegisterAddress)
{
volatile uint8_t result=0;
I2C0_MSA_R = 0x000000A6; //write operation
I2C0_MDR_R = RegisterAddress; //place data to send mdr register
I2C0_MCS_R = 0x00000007; //stop start run
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
I2C0_MSA_R = 0x000000A7; // read operation
I2C0_MCS_R = 0x00000007; // stop start run
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
result = I2C0_MDR_R;
return result;
}
void WriteRegister(uint8_t RegisterAddress,uint8_t Data)
{
I2C0_MSA_R = 0x000000A6; //write operation
I2C0_MDR_R = RegisterAddress; //place register address to set in mdr register
I2C0_MCS_R = 0x00000003; //burst send ( multiple bytes send )
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
I2C0_MDR_R = Data; //place data to be sent in mdr register
I2C0_MCS_R = 0x00000005; // transmit followed by stop state
while((I2C0_MCS_R &= 0x00000040)==1); //poll busy bit
}
Your WriteRegister and ReadRegister functions do not follow the flowcharts defined in the TM4C123G data sheet. Apart from not checking or handling the MCS ERROR flag, Figure 16-10 Master TRANSMIT of Multiple Data Bytes shows that when writing the MCS register, you should assert specific bits, while you are writing to all bits, You should instead perform a read-modify-write:
I2C0_MCS_R = 0x00000003; //burst send ( multiple bytes send )
should be:
// I2CMCS = ---0-011
uint32_t mcs = I2C0_MCS_R ;
msc &= ~0x00000014; // ---0-0--
mcs |= 0x00000003; // ------11
I2C0_MCS_R = mcs ;
And similarly:
I2C0_MCS_R = 0x00000005; // transmit followed by stop state
should be
// I2CMCS = ---0-101
mcs = I2C0_MCS_R ;
mcs &= ~0x00000012; // ---0--0-
mcs |= 0x00000005; // -----1-1
I2C0_MCS_R = mcs ;
ReadRegister() has a similar issue (although it is unlikely to be an issue in this case):
I2C0_MCS_R = 0x00000007; //stop start run
should strictly be:
// I2CMCS = ---00111
uint32_t mcs = I2C0_MCS_R ;
mcs &= ~0x00000018; // ---00---
mcs |= 0x00000007; // -----111
I2C0_MCS_R = mcs ;
The datasheet recommends for bits 31:5:
Software should not rely on the value of a reserved bit. To provide
compatibility with future products, the value of a reserved bit should
be preserved across a read-modify-write operation.
The above code does that, but in practice should not be necessary on this specific product, but is good practice in any case.
In any event you should add the recommended error handling code. It may be that no error flag is being set, but we don't know that unless you check for it, and doing so will at least assist debugging - rather then stepping the code, you can simply set a break-point on the error handling and then run at full-speed. This will narrow down the number of possibilities.
as #Clifford had explained that i should follow the flow charts and although his answer is completely correct it didn't give me any results (previously gave values in case of stepping into the function gave zeroes afterwards) but , i noticed something in the flow charts that i hadn't noticed before which contradicts with the initialization and configuration section in the data sheet
now as it says in step 11 that you should be polling the bus busy bit in the MCS register but this is wrong and contradicts with the flow charts , the flow charts are more correct as u should check if the bus is busy before sending anything and then check for the master busy bit before reading from the MDR register or moving on to execute and further steps
basically the correct steps in the initialization and configuration should be :
before step 10 poll the bus busy bit in case any other master is sending which can be omitted in case of a single master
after step 10 poll the busy bit before reading or going to any further step to conclude whether the sending has been completed and the master is idle or not
i'm sorry i feel like a complete idiot now for not reading the flow charts carefully but i followed another part which is the initialization and configuration part accepting a fact which wasn't there that both should imply the same thing .
i also found that it works correctly in the tivaware API following the flow charts and not that other section in the datasheet however i didn't want to use the Tivaware API as i am looking forward for problems like this which lead to a better understanding of how things work
thanks again for your help #Clifford cheers!
So I'm having a problem getting a TI microcontroller to communicate with the Raspberry Pi B+. The exact microcontroller I'm using is the TI cc430f5137. The issue I'm having is that I just can't seem to get the Raspberry Pi to correctly receive the data I'm sending from the MSP430. For those who don't know, the 430 has 2 buffers for this purpose, a RX and TX, which allows the use of the UART module while code is still executing. I've enabled an interrupt for when I receive a byte, and I simply set a flag and send the same byte right back. It works up until I attempt to transmit.
The code sits and waits in an infinite loop until it receives it's first byte. At that point it simply saves the byte and flashes the LED if it's a 'T' (for testing). Upon returning to the loop, it detects that the saved byte has changed, and puts it in the buffer to send it back. Until this point, everything works perfectly. It receives the correct byte every time, letting me know my clocks are perfect, my interrupt is working, and my UART initialization is correct. Where it goes wrong is after sending the byte, it seems like there is some kind of internal loopback (this is an option but I made sure this is not the case) that is causing the interrupt to re-trigger, resulting in an infinite loop of transmitting and again receiving the same byte, but upon invoking this via the Pi I don't get back a loop of the same character, but instead a byte of random garbage that has no consistency or logic behind it. I analyzed the bits to see if the timing is just off and that doesn't seem to be the case. For reference, my Baud is a measly 1200, the voltage of both devices is definitely 3.3v, and I'm sure the Pi is working because when I short the RX and TX, I get back the byte without an issue. I switched to UART because SPI was giving me similar problems, and I can't think of any other protocol besides I2C that would help here. I am using an external 32768hz crystal. Also, I've tried this on two different microcontrollers, so its definitely the code that's the issue.
#include <msp430.h>
char temp;
char in;
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
P1OUT = 0x00; // Make sure pins are tturned off
P1DIR = 0x01; // Led out
P1SEL |= BIT5 + BIT6; // UART as pin mode
UCSCTL6 &= ~BIT0; // Turn on XT1
P5SEL |= BIT0 + BIT1; // Select XT1 as pin function
UCA0CTL1 |= BIT0; // Set UART to reset mode
UCA0CTL1 |= BIT6; // Choose ACLK as source
UCA0BR0 = 27; // Set speed to 1200 Baud
UCA0MCTL = 0x02 << 1; // Set speed to 1200 Baud
UCA0CTL1 &= ~BIT0; // Turn UART on
UCA0IE = BIT0; // Enable RX interrupt
__enable_interrupt();
while(1)
{
if(in != 0)
{
UCA0TXBUF = in;
temp = in;
in = 0;
}
}
}
#pragma vector=USCI_A0_VECTOR
__interrupt void UCSIA0(void)
{
in = UCA0RXBUF;
if(in == 0x54)
P1OUT ^= BIT0;
}
Output from running minicom at 1200 on Pi, Sending 'T' one at a time:
UÔÿÿïÕuU_þýÿÿÿÿÿÿÕԯÿÿôÕüÿÝUõï\þþÿÿÕ¿ÿÿýýTÿýUÿÿÿïÿÿÿõÿýýÿõûÿ
assuming Pi is working currectly...
1.verify msp430 TX is woring: send every 1 sec known value and see if PI getting it currectly.
2.verify MSP430 RX working: send from Pi known value every 1 sec.
3.interrupt section:
your code dosent verfiy that RX interrupt is off.
you should filter interrupts generated only for the RX .
also, your code dont handle overrun/frame errors.
sharing "in" variable both for TX and RX (and both at interrupt and main loop section)-not good idea..
4.your output example suggests that you have baud rate mismatch issue.
if you send character 'T' and shoud get back 'T'. i expect to see 'TTTTTT...'
BTW this garbage may suggests that you forgot to connecting GND line between two MCUs...