Arduino port relocation (PORTD to PORTB) - c

So I'm trying to connect the chip with an 8-bit data bus. All works good, but...
Arduino Uno uses D0 and D1 for serial TX/RX (USB connection with PC). When I try to open a serial connection (on serial.begin stage) ports D0 and D1 blocks and the chip stops working. This is a problem.
I think I should relocate pins D0 and D1 to another port. But the chip uses 8 pins (8bit chip). So I need to relocate the first pin (D0) and the second pin (D1) to port b. Maybe I can use B4 and B3?
But I don't know how to use PORTD[PD2, PD3, PD4, PD5, PD6, PD7] and PORTB[PB4, PB3] in one time like:
var portX = PORTD[PD2, PD3, PD4, PD5, PD6, PD7] + PORTB[PB4, PB3];
portX = data;
My code:
#include <avr/io.h> // For I/O and other AVR registers
#include <util/delay.h> // For timing
/* Pinmap (Arduino UNO compatible) */
#define YM_IC (5) // PC5 (= pin A5 for Arduino UNO)
#define YM_CS (4) // PC4 (= pin A4 for Arduino UNO)
#define YM_WR (3) // PC3 (= pin A3 for Arduino UNO)
#define YM_RD (2) // PC2 (= pin A2 for Arduino UNO)
#define YM_A0 (1) // PC1 (= pin A1 for Arduino UNO)
#define YM_A1 (0) // PC0 (= pin A0 for Arduino UNO)
#define YM_CTRL_DDR DDRC
#define YM_CTRL_PORT PORTC
#define YM_DATA_DDR DDRD
#define YD0 (0)(= pin D0 for Arduino UNO)
#define YD1 (1)(= pin D1 for Arduino UNO)
#define YD2 (2)(= pin D2 for Arduino UNO)
#define YD3 (3)(= pin D3 for Arduino UNO)
#define YD4 (4)(= pin D4 for Arduino UNO)
#define YD5 (5)(= pin D5 for Arduino UNO)
#define YD6 (6)(= pin D6 for Arduino UNO)
#define YD7 (7)(= pin D7 for Arduino UNO)
#define YM_DATA_PORT PORTD // Whole PORT D for data bus (= pins D0 to D7 for Arduino UNO)
#define YM_MCLOCK (1) // PB1 = OC1A (= pin D9 for Arduino UNO)
#define YM_MCLOCK_DDR DDRB
extern uint8_t chflash[6];
uint8_t dacflash;
#define NOTEONFLASH 4
#define VGMWAIT 15
static void write_ym(uint8_t data) {
YM_CTRL_PORT &= ~_BV(YM_CS); // CS LOW
YM_DATA_PORT = data;
_delay_us(1);
YM_CTRL_PORT &= ~_BV(YM_WR); // Write data
_delay_us(5);
YM_CTRL_PORT |= _BV(YM_WR);
_delay_us(5);
YM_CTRL_PORT |= _BV(YM_CS); // CS HIGH
}
/**
* Write data into a specific register of the YM2612
*
* #author Furrtek
* #param reg Destination register address
* #param data Data to write
*/
static void setreg(uint8_t reg, uint8_t data) {
YM_CTRL_PORT &= ~_BV(YM_A0); // A0 low (select register)
write_ym(reg);
YM_CTRL_PORT |= _BV(YM_A0); // A0 high (write register)
write_ym(data);
}
void call() {
setreg(0x28, 0xF0); // Key on
_delay_ms(1000);
setreg(0x28, 0x00); // Key off
}
uint8_t getfilebyte() {
return Serial.read();
}
int main() {
init();
//Serial.begin(9600);
/* Pins setup */
YM_CTRL_DDR |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD) | _BV(YM_A0) | _BV(YM_A1);
YM_DATA_DDR |= _BV(YD0) | _BV(YD1) | _BV(YD2) | _BV(YD3) | _BV(YD4) | _BV(YD5) | _BV(YD6) | _BV(YD7);
YM_MCLOCK_DDR |= _BV(YM_MCLOCK);
YM_CTRL_PORT |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD); /* IC, CS, WR and RD HIGH by default */
YM_CTRL_PORT &= ~(_BV(YM_A0) | _BV(YM_A1)); /* A0 and A1 LOW by default */
/* F_CPU / 2 clock generation */
TCCR1A = _BV(COM1A0); /* Toggle OCA1 on compare match */
TCCR1B = _BV(WGM12) | _BV(CS10); /* CTC mode with prescaler /1 */
TCCR1C = 0; /* Flag reset */
TCNT1 = 0; /* Counter reset */
OCR1A = 0; /* Divide base clock by two */
/* Reset YM2612 */
YM_CTRL_PORT &= ~_BV(YM_IC);
_delay_ms(10);
YM_CTRL_PORT |= _BV(YM_IC);
_delay_ms(10);
/* YM2612 Test code */
setreg(0x22, 0x00); // LFO off
setreg(0x27, 0x00); // Note off (channel 0)
setreg(0x28, 0x01); // Note off (channel 1)
setreg(0x28, 0x02); // Note off (channel 2)
setreg(0x28, 0x04); // Note off (channel 3)
setreg(0x28, 0x05); // Note off (channel 4)
setreg(0x28, 0x06); // Note off (channel 5)
setreg(0x2B, 0x00); // DAC off
setreg(0x30, 0x71); //
setreg(0x34, 0x0D); //
setreg(0x38, 0x33); //
setreg(0x3C, 0x01); // DT1/MUL
setreg(0x40, 0x23); //
setreg(0x44, 0x2D); //
setreg(0x48, 0x26); //
setreg(0x4C, 0x00); // Total level
setreg(0x50, 0x5F); //
setreg(0x54, 0x99); //
setreg(0x58, 0x5F); //
setreg(0x5C, 0x94); // RS/AR
setreg(0x60, 0x05); //
setreg(0x64, 0x05); //
setreg(0x68, 0x05); //
setreg(0x6C, 0x07); // AM/D1R
setreg(0x70, 0x02); //
setreg(0x74, 0x02); //
setreg(0x78, 0x02); //
setreg(0x7C, 0x02); // D2R
setreg(0x80, 0x11); //
setreg(0x84, 0x11); //
setreg(0x88, 0x11); //
setreg(0x8C, 0xA6); // D1L/RR
setreg(0x90, 0x00); //
setreg(0x94, 0x00); //
setreg(0x98, 0x00); //
setreg(0x9C, 0x00); // Proprietary
setreg(0xB0, 0x32); // Feedback/algorithm
setreg(0xB4, 0xC0); // Both speakers on
setreg(0x28, 0x00); // Key off
setreg(0xA4, 0x22); //
setreg(0xA0, 0x69); // Set frequency
call();
while(1000) {
if (Serial.available() > 0) {
_delay_ms(1000);
call();
_delay_ms(1000);
char var = getfilebyte();
Serial.print("Yeah: ");
Serial.println(var);
} else {
Serial.print("Nooo: ");
Serial.println(Serial.available());
call();
}
}
while(1);
}

You need bitmath to put them together. Bitshift and bitwise or and bitwiase and. Look those up in the reference. What you want will look something like:
byte portX = (PORTD & 0xFC) | ((PORTB >> 3) & 0x03);
What you actually end up with might be slightly different though since I am making assumptions about what order you want the bits in. The first part masks off the high 6 bits of PORTD, the second part shift the two bits you want out of PORTB into the lowest two bits and masks them off. The | in the middle puts the two together.

A seceond answer because I think you're actually going the other way now that I re-read your question. I think you want to output to those pins. You'll need bitmath to do that as well.
Something like:
PORTB |= (data & 0x03) << 3;
PORTD |= data & 0xFC;

"There is no way to write to two different registers in the same instruction, so if you need all eight bits to change exactly at the same time you're out of luck." Answer from electronics. Maybe i can test my luck in multiple serial begin and end..

Related

Unable to read multiple channels from single ADC with DMA [bare metal]

I'm trying to read two ADC channels sequentially from my STM32F407ZGT6 using DMA. I'm just trying to get the values from two potentiometers independently on each channel. Although the program doesn't crash, I does not update my variable's value (sensor_val).
I'm using DMA2_Stream0 Channel 0, since I'm using ADC1. For my ADC1, I'm using PB1 (channel 9) and PA1 (channel 1). I tried to follow this tutorial, except that I do not want to trigger my ADC from a timmer just yet, and I've been also checking the example on this question. My ADC callback also never gets called. As far as my understanding goes, the sequence of calls should be:
ADC1->SR EOC --> ADC->CR1 EOCIE --> DMA2_Stream0_IRQHandler() --> dma_ADC_callback()┐
⮤─────────────────────────────────────────────────────────┘
Maybe I do need to include a periodic call to read the ADC?
All the ADC/DMA functions are on their separate .c/.h files. I've already tried to declare sensor_val as a global variable or as an extern variable from the adc.c file, and both give the same result. Here is an approximate mwe of my code:
#include <stdint.h> //uint32_t
#include <stdio.h> //printf
#include "stm32f407xx.h"
#define set(val, pos) ((val) << (pos))
#define msk(size, pos) (((1UL << (size)) - 1UL) << (pos))
static uint32_t sensor_val[2];
static void dma_ADC_callback(void);
void gpio_init(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Port A
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Port B
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Port C
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; //Port D
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; //Port F
RCC->AHB1ENR;
//PORT B
GPIOA->MODER |= set(m,(2)); //ADC || PA1 || ADC123_IN1
GPIOB->MODER |= set(m,(2)); //ADC || PB1 || ADC12_IN9
}
void adc_init(void)
{
/*Enable clock access to ADC*/
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;
// RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;
/* Config ADC parameters*/
/* Regular Sequence Register 3
* Since the sequence starts from
* the back, we need to set channels
* from SQ3[0] to SQ1[19]
*/
ADC1->SQR3 |= set(0b1001,0); //sets channel PB1 (ADC12_IN9) as 1st conversion
ADC1->SQR3 |= set(0b0001,5); //sets channel PA1 (ADC123_IN1) as 2nd conversion
ADC1->SQR1 |= set(0b0001,ADC_SQR1_L_Pos); //tells the channel sequence lenght = 2
/*If using more than one channel
* SCAN is required
*/
ADC1->CR1 |= set(1,ADC_CR1_SCAN_Pos);
/*Adjust ADC sample time
* The resulting frequency is
* APB2/#cycles:
* 000: 3 cycles
* 001: 15 cycles
* 010: 28 cycles
* 011: 56 cycles
* 100: 84 cycles
* 101: 112 cycles
* 110: 144 cycles
* 111: 480 cycles = 42MHz/480 = 87.5kHz
* */
ADC1->SMPR2 |= set(0b111,ADC_SMPR2_SMP0_Pos); //channel 0
ADC1->SMPR2 |= set(0b111,ADC_SMPR2_SMP9_Pos); //channel 9
/*Turn Interruption On*/
ADC1->CR1 |= set(1,ADC_CR1_EOCIE_Pos);
/*Enable ADC*/
ADC1->CR2 |= set(1,ADC_CR2_ADON_Pos);
}
void adc_start_conversion(void)
{
ADC1->CR2 |= set(1,ADC_CR2_EOCS_Pos); //enables multi-channel conversion
ADC1->CR2 |= set(1,ADC_CR2_CONT_Pos); //enables continuous conversion
ADC1->CR2 |= set(1,ADC_CR2_SWSTART_Pos); //starts conversion
}
/*ADC1 DMA2 => DMA2_Ch0_Stream0 and 4*/
/*ADC2 DMA2 => DMA2_Ch1_Stream2 and 3*/
/*ADC3 DMA2 => DMA2_Ch2_Stream0 and 1*/
void dma2_stream0_init(uint32_t memo, uint32_t periph, uint32_t len)
{
/*Enable clock acces to DMA*/
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
/*Diable DMA2 Stream 0*/
DMA2_Stream0->CR &= ~DMA_SxCR_EN;
/*Clear all interrupt flags of Stream 0*/
DMA1->LIFCR |= DMA_LIFCR_CFEIF0;
DMA1->LIFCR |= DMA_LIFCR_CDMEIF0;
DMA1->LIFCR |= DMA_LIFCR_CTEIF0;
DMA1->LIFCR |= DMA_LIFCR_CHTIF0;
/*Set the source buffer*/ //Memory Address
DMA2_Stream0->M0AR = memo;
/*Set destination buffer*/ //Peripherial Address
DMA2_Stream0->PAR = periph;
/*Set the length*/
DMA2_Stream0->NDTR = len;
/*Set Control options
* Select Stream0_CH0 |
* Prioritu Lvl = High |
* Memory Increment On |
* Circular mode on |
* Direction Per->Mem (0b00)|
* Enable Transfer Complete interrupt
*/
DMA2_Stream0->CR &= ~(DMA_SxCR_CHSEL |
DMA_SxCR_PL |
DMA_SxCR_MSIZE |
DMA_SxCR_PSIZE |
DMA_SxCR_PINC);
DMA2_Stream0->CR |= (set(0,DMA_SxCR_CHSEL_Pos) |
set(2,DMA_SxCR_PL_Pos) |
set(1,DMA_SxCR_MINC_Pos) |
set(1,DMA_SxCR_CIRC_Pos) |
set(0,DMA_SxCR_DIR_Pos) |
set(1,DMA_SxCR_TCIE_Pos)
);
/*Enable direct mode and disable FIFO*/
DMA2_Stream0->FCR = 0;//set(0,DMA_SxFCR_FEIE_Pos);
/*!! Enable DMA1 Stream 6*/
DMA2_Stream0->CR |= set(1,DMA_SxCR_EN_Pos);
/*Enable ADC transmitter DMA*/
ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos);
/*DMA Interrupt enable in NVIC*/
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
void DMA2_Stream0_IRQHandler(void)
{
/*Check for transfer complete interrupt*/
if(DMA2->LISR & msk(1,DMA_LISR_TCIF0_Pos))
{
//Clear flag
DMA2->LIFCR |= msk(1,DMA_LIFCR_CTCIF0_Pos);
//Callback
dma_ADC_callback();
}
}
static void dma_ADC_callback(void)
{
printf("[ Readings Pots.: | %li | %li ]\r\n", sensor_val[0], sensor_val[1]);
}
int main(void)
{
/*Setup*/
//clock_init_168(); //SYSCLK = 168MHz, AHB = 84MHz, APB1 = 42MHz, APB2 = 84MHz
//init_systick_MS(SYSTICK_LOAD_VAL_MS);
gpio_init();
dma2_stream0_init((uint32_t)&sensor_val, (uint32_t)&ADC1->DR, 2);
adc_init();
adc_start_conversion(); //continuous conversion
char count = 0;
for(;;)
{
debug_msg("count : %d", __PRETTY_FUNCTION__, count);
delayMS(500);
count++;
}
}
I finally found a way to fix it!
First of all, the ADC has to be enabled before the DMA. With the code above, ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos) wouldn't get set because I was trying to set the DMA before the ADC. Hence, my interruption never got called. The correct order of function calls is:
adc_init();
dma2_stream0_init((uint32_t)&sensor_val, (uint32_t)&ADC1->DR, 2);
adc_start_conversion();
However, after ADC_CR2_DMA was getting set, my callback got called, but only once. So I had to disable DMA selection with ACD1->CR2 = ADC_CR2_DDS, so DMA would issue conversion requests recurrently:
/*Enable ADC transmitter DMA*/
ADC1->CR2 |= (set(1,ADC_CR2_DMA_Pos) | //<<make sure ADC is already enabled
set(1,ADC_CR2_DDS_Pos)); //<<without DDS, the DMA does a single conversion
Once I did that, my callback got called and I was reading data, but only sensor_val[0] was being updated. That was happening because I had PSIZE == 00, which is the value used to set MSIZE in direct mode. Since I'm using DMA for ADC (maximum 12-bit resolution), I changed sensor_val to uint16_t and PSIZE = 0b01:
DMA2_Stream0->CR |= (set(0,DMA_SxCR_CHSEL_Pos) |
set(2,DMA_SxCR_PL_Pos) |
set(1,DMA_SxCR_PSIZE_Pos) | //<<sets MSIZE=PSIZE in direct mode
set(1,DMA_SxCR_MINC_Pos) |
set(1,DMA_SxCR_CIRC_Pos) |
set(0,DMA_SxCR_DIR_Pos) |
set(1,DMA_SxCR_TCIE_Pos));
And voilá!
NOTES
PINC should be kept at reset value for this configuration, otherwise ADC will expect data with more than 16-bits.
If you need slower rates of acquisition, instead of setting DDS, a DMA request function can be created where the DMA bit is reset and set again:
void adc_new_dma_conversion(void)
{
/*DMA has to be reset first
* then re-enabled to generate
* a new DMA request
*/
ADC1->CR2 &= ~set(1,ADC_CR2_DMA_Pos);
ADC1->CR2 |= set(1,ADC_CR2_DMA_Pos);
}

STM32 UART2 BRR Not Giving Expected Results

I am currently working with an STM32F407VG on the Discovery board. I am going through the peripherals and trying to get each one working by manipulating the registers only (no HAL).
When I go to initialize UART2 it is transmitting the character I write to the DR but it is doing so ~12x faster than expected, I'm shooting for 9600 baud. I measured this using an oscilloscope (~8-9 us per bit) and playing with the baud rate in Putty (111,111 baud to show the actual character).
I am maxing out the clock speed of the chip 168 MHz SYSCLK, APB1 Prescaler "/4", APB2 Prescaler "/2". I am pretty sure my clocks are at what they are supposed to be, to verify I set up TIMER12, which shares the APB1 clock and set the prescaler to 8400 and had an interrupt generated every time there was a compare match (CCR = 5000) and overflow. I measured this with the oscilloscope and I am getting a 1 Hz square wave as expected which means that the APB1 for Timer 12 is at 84 MHz.
Here is my clock init code:
void SysClockConfig ( void ){
//setting up the MCO output to see the clock signal
//RCC->CFGR |= ( 6 << 24 ) | (3 << 21 ) | ( 4 << 27 );
//1. ENABLE HSE and wait for the HSE to become Ready
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
// 2. Set the POWER ENABLE CLOCK and VOLTAGE REGULATOR
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
// 3. Configure the FLASH PREFETCH and the LATENCY Related Settings
FLASH->ACR = FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_PRFTEN | FLASH_ACR_LATENCY_5WS;
//4. Configure the PRESCALARS HCLK, PCLK1, PCLK2
// AHB PR
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
// APB1 PR
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
// APB2 PR
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
//5. Configure the MAIN PLL
RCC->PLLCFGR = (PLL_M << RCC_PLLCFGR_PLLM_Pos) | (PLL_N << RCC_PLLCFGR_PLLN_Pos) | (PLL_Q << RCC_PLLCFGR_PLLQ_Pos) | (RCC_PLLCFGR_PLLSRC_HSE);
//6. Enable the PLL and wait for it to become ready
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
//7. Set source
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
This is my UART2 config code:
void UART2_Config ( void )
{
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; //clock UART
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //clock GPIOA
GPIOA->MODER |= ( 2 << GPIO_MODER_MODER2_Pos ) | ( 2 << GPIO_MODER_MODER3_Pos ); //set PA2 and PA3 alternate function
GPIOA->OSPEEDR |= ( 3 << GPIO_OSPEEDR_OSPEED2_Pos ) | ( 3 << GPIO_OSPEEDR_OSPEED3_Pos ); //clock GPIO pin at fastest speed
GPIOA->AFR[0] |= ( 7 << GPIO_AFRL_AFSEL2_Pos ) | ( 7 << GPIO_AFRL_AFSEL3_Pos ); //set PA2 and PA3 to alt func UART2
USART2->CR1 = 0;
USART2->CR1 |= USART_CR1_UE; //UART Enable
//USART2->BRR = (0x16 << USART_BRR_DIV_Mantissa_Pos) | (0xc << USART_BRR_DIV_Fraction_Pos); //Set Baud rate
USART2->BRR = 4300;
//USART2->CR1 |= USART_CR1_RE; //Receiver enable
USART2->CR1 |= USART_CR1_TE; //Transmitter enable
//Baud rate is off by a factor of 12ish
}
Finally, my main and while loop with the function for sending a character:
void UART2_SendChar ( uint8_t c )
{
USART2->DR = c;
while ( !(USART_SR_TC));
}
int main ( void )
{
SysClockConfig();
GPIO_Config();
TIM10_Config();
TIM12_Config();
UART2_Config();
//NVIC_SetPriority (TIM1_UP_TIM10_IRQn, 1);
//NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
NVIC_SetPriority (TIM8_BRK_TIM12_IRQn, 1);
NVIC_EnableIRQ(TIM8_BRK_TIM12_IRQn);
while ( 1 )
{
//GPIOD->BSRR |= (1<<12);
//delay(2000000);
//GPIOD->BSRR |= (1<<28);
delay(4000000);
UART2_SendChar('a');
}
}
I can't find anything in the reference manual to explain this behavior. That tells me I am doing something wrong but I can't seem to track it down. On a final note, I had Putty set up to receive 9600 baud and played around with the BRR and setting it to a value of 4300 output the desired character. Plugging that value into the equation for baud in the reference manual gave me an insane system clock 660 MHz, again telling me I am missing something probably pretty obvious.
I've answered a similar question here recently, so let me copy that one:
BRR is a Q12.4 format fixed point number, which needs one additional operation when OVER8 mode is selected.
OP's question is about STM32F4, but if one inspects STM32F030 reference manual, he/she can find the same logic but represented by a different formula. IMO, that one (F030) is more clear and easier to understand.
To convert uint16_t (Q16.0) to Q12.4, you need to multiply it by 16.
Basically, for OVER16 case, BRR is simply becomes PeripheralClock / BaudRate.
For OVER8 case, you first need to calculate a temporary variable as temp = 2 * PeripheralClock / BaudRate. When writing it into BRR, you need to right-shift its lower 4 bits once.
See the example code:
void setBaudRate(uint32_t baud, bool over8)
{
const uint32_t pClock = 42000000ull; // Hard-code or call a function to obtain it
uint32_t usartDiv = (over8) ? (2 * pClock / baud) : (pClock / baud);
uint32_t reg = USART1->BRR;
reg &= ~0xffff; // Clear the lower 16 bits
if (over8) {
reg |= (usartDiv & 0xfff0) | ((usartDiv & 0xf) >> 1);
}
else { // over16
reg |= usartDiv;
}
USART1->BRR = reg;
}
So, for your case, the correct value of BRR is 4375.

Data Not Being Stored in Receive Data Register, (UART RXNE Flag not setting)

I have been programming the stm32l412kb nucleo board, attempting to achieve basic UART communication. Transmission from the board works great but the board is not appearing to receive any data.
For the software side, I have tried using standard HAL code in a few ways different, in both interrupt and non-interrupt mode. I have tied a more basic approach (shown below). From debugging line by line I have found that the Receive Data register (RDR) is not filling (and consequently the flag which sets when there is data there is not setting). This has been the error in each case.
This aim of this code is to send back the character entered.
#include "stm32l4xx.h"
int main(void)
{
/* USER CODE BEGIN 1 */
/*The Usart2 peripheral needs its clock to be enabled.*/
RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN;
RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN;
/*The 72 MHz APB1 bus clock with a 9600baud rate gives a baud rate for the register of 0x1D4C*/
USART2->BRR = 0x1D4C;
/*For USART2 we need to enable the overall UART (U) driver, the transmission lines(T) and the reading lines(R). UART Enable is last.*/
USART2->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
/*Setting transmission pin*/
GPIOA->MODER |= GPIO_MODE_AF_PP;
GPIOA->OSPEEDR |= GPIO_SPEED_FREQ_HIGH;
/* USER CODE END 1 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (USART2->ISR & USART_ISR_RXNE) //if RX is not empty
{
char temp = USART2->RDR; //fetch the data received
USART2->TDR = temp; //send it back out
while (!(USART2->ISR & USART_ISR_TC)); //wait for TX to be complete
}
}
return 0;
}
To send the data I have used RealTerm Serial Capture and have tried also the stm32cubeIDE console. One possibility of the source of the problem is that the DataSheet says
"In the USART, the start bit is detected when a specific sequence of samples is recognized. This sequence is: 1 1 1 0 X 0 X 0 X 0 0 0 0."
I have not coded any way of leading my data with this, however, from all the examples I have seen from a couple of books as well as videos, they did not need to think about this and it worked perfectly. Could it be a hardware problem? Is there something I'm not initialising? I have even tried different cables.
Many thanks in advance for any help,
Harry
/*********************************UPDATE**************************************/
First and foremost, thank you very much for the help, I now understand basics such as how to use the datasheet to configure the registers. It is much appreciated. I have updated my code but still the problem remains.
So I have updated my configuration as so:
/*Configuring GPIO Pins*/
/*Clearing whatever is held in the mode registers for pins 2 and 3 (Inverting with their masks.)*/
GPIOA -> MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
/*The 2 bits 10 are being shifted to the position which configures Mode of pin 2 and also for pin 3 in the mode register.
*(10 is alterntive function mode).*/
GPIOA -> MODER |= (0b10 << GPIO_MODER_MODE2_Pos) | (0b10 << GPIO_MODER_MODE3_Pos);
/*Clearing whatever is held in the output speed registers for pins 2 and 3*/
GPIOA -> OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2_Msk | GPIO_OSPEEDR_OSPEED3_Msk);
/*Setting the speed of pins 2 and 3 to be very high(11)*/
GPIOA -> OSPEEDR |= (0b11 << GPIO_OSPEEDR_OSPEED2_Pos) | (0b11 << GPIO_OSPEEDR_OSPEED3_Pos);
/*Clearing whatever is held in the alternative function registers for pins 2 and 3.*/
GPIOA -> AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk);
/*Setting the pins 2 and 3 to their alternative functions(TX and RX)*/
GPIOA -> AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);
/*Clock Configuration*/
/*Enabling the USART2 peripheral clock.*/
RCC->APB1ENR1 &= ~(RCC_APB1ENR1_USART2EN_Msk);
RCC->APB1ENR1 |= (0b1 << RCC_APB1ENR1_USART2EN_Pos);
/*Enabling the GPIOA port peripheral clock*/
RCC->AHB2ENR &= ~(RCC_AHB2ENR_GPIOAEN_Msk);
RCC->AHB2ENR |= (0b1 << RCC_AHB2ENR_GPIOAEN_Pos);
/*USART Configuartion*/
/*The 72 MHz APB1 bus clock with a 9600baud rate gives a baud rate for the register of 0x1D4C*/
USART2->BRR = 0x1D4C;
/*For USART2 we need to enable the overall UART (U) driver, the transmission lines(T) and the reading lines(R). UART Enable is last.*/
USART2->CR1 &= ~(USART_CR1_RE_Msk | USART_CR1_TE_Msk | USART_CR1_UE_Msk);
USART2->CR1 |= USART_CR1_RE | USART_CR1_TE | USART_CR1_UE;
Which has greatly developed my understanding of how to properly configure the device. However, I'm still having a problem with the overall aim of the code to bounce back a character, as the data is still not being read by the MCU. I will pursue on and update if it's successful. I'm thankful for any further suggestions.
this does not initialize the GPIO MODER or OPEEDR regiters.
GPIOA->MODER |= GPIO_MODE_AF_PP;
GPIOA->OSPEEDR |= GPIO_SPEED_FREQ_HIGH;
GPIO_MODE_AF_PP & GPIO_SPEED_FREQ_HIGH are HAL definitions and cant be used on the register level.
You need to set the appropriate values for every pin you use:
It will never receive or send anything as you forgot to set the GPIO -> AF registers and the hardware is not connected to the pins internally.
You can find the alternate functions mappin in the Datasheet
and the AF GPIO registers in the Reference Manual
this sequence should be:
GPIOA -> MODER &= ~(GPIO_MODER_MODE2_Msk | GPIO_MODER_MODE3_Msk);
GPIOA -> MODER |= (0b10 << GPIO_MODER_MODE2_Pos) | (0b10 << GPIO_MODER_MODE3_Pos);
GPIOA -> OSPEEDR &= ~(GPIO_OSPEEDR_OSPEED2_Msk | GPIO_OSPEEDR_OSPEED23Msk);
GPIOA -> OSPEEDR |= (0b11 << GPIO_OSPEEDR_OSPEED2_Pos) | (0b11 << GPIO_OSPEEDR_OSPEED3_Pos);
GPIOA -> AFR[0] &= ~(GPIO_AFRL_AFSEL2_Msk | GPIO_AFRL_AFSEL3_Msk);
GPIOA -> AFR[0] |= (7 << GPIO_AFRL_AFSEL2_Pos) | (7 << GPIO_AFRL_AFSEL3_Pos);

Getting DMA USART to work on STM32L053R8T6

I am having an issue in getting my computer (virtual COM port, to be exact) to communicate with my STM32L053R8T6 (Nucleo) board by DMA and USART. Here is my code for the DMA and USART part:
#include "Device/Include/stm32l0xx.h" // Device header
#include "JB.h"
#include <string.h>
#define PCLK 32000000
#define BAUD 19200
uint8_t stringtosend[] = "test\n";
uint8_t stringtoreceive[] = " ";
void ENABLE_UART_DMA(void){
RCC->AHBENR |= RCC_AHBENR_DMA1EN; //enable periph.clk for DMA1
/**Enabling DMA for transmission
* DMA1, Channel 4 mapped for USART2TX
* USART2 TDR for peripheral address
* stringtosend for data address
* Memory increment, memory to peripheral | 8-bit transfer | transfer complete interrupt**/
DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C4S) | (4 << (3 * 4));
DMA1_Channel4->CPAR = (uint32_t)&(USART2->TDR);
DMA1_Channel4->CMAR = (uint32_t)stringtosend;
DMA1_Channel4->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE;
/**Enabling DMA for reception
* DMA1, Channel 5 mapped for USART2RX
* USART2 RDR for peripheral address
* stringtoreceive for data address
* Data size given
* Memory increment, peripheral to memory | 8-bit transfer | transfer complete interrupt**/
DMA1_CSELR->CSELR = (DMA1_CSELR->CSELR & ~DMA_CSELR_C5S) | (4 << (4 * 4));
DMA1_Channel5->CPAR = (uint32_t)&(USART2->RDR);
DMA1_Channel5->CMAR = (uint32_t)stringtoreceive;
DMA1_Channel5->CNDTR = sizeof(stringtoreceive);
DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_TCIE | DMA_CCR_EN;
NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 0); //NVIC enabled, max priority, channels 4-7
NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn);
}
void CONFIGURE_UART_PARAM(void){
RCC->IOPENR |= ( 1ul << 0); //Enable GPIOA clock
RCC->APB1ENR |= ( 1ul << 17); //Enable USART#2 clock
GPIOA->AFR[0] &= ~((15ul << 4* 3) | (15ul << 4* 2) ); //Clear PA2,PA3
GPIOA->AFR[0] |= (( 4ul << 4* 3) | ( 4ul << 4* 2) ); //Set PA2,PA3
GPIOA->MODER &= ~(( 3ul << 2* 3) | ( 3ul << 2* 2) ); //Same as above
GPIOA->MODER |= (( 2ul << 2* 3) | ( 2ul << 2* 2) );
USART2->BRR = PCLK/BAUD;
USART2->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; //Enable DMA mode in transmit and receive
/*UART enabled for transmission and reception*/
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
while((USART2->ISR & USART_ISR_TC) != USART_ISR_TC)
{
/* add time out here for a robust application */
}
USART2->ICR = USART_ICR_TCCF;
}
void CONFIGURE_EXTI(void){
SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
EXTI->FTSR |= EXTI_FTSR_TR13; //falling edge trigger
EXTI->IMR |= EXTI_IMR_IM13; //unmask
NVIC_SetPriority(EXTI4_15_IRQn, 0); //def interrupt
NVIC_EnableIRQ(EXTI4_15_IRQn);
}
/*************************************************************************************************************************************************************************************************************************/
/*************************************************************************************************************************************************************************************************************************/
/*Interrupt Handlers*/
void DMA1_Channel4_5_6_7IRQHandler(void){
if((DMA1->ISR & DMA_ISR_TCIF4) == DMA_ISR_TCIF4){
DMA1->IFCR = DMA_IFCR_CTCIF4; //Clear Channel 4 Transfer Complete flag
}
else if((DMA1->ISR & DMA_ISR_TCIF5) == DMA_ISR_TCIF5){
DMA1->IFCR = DMA_IFCR_CTCIF5; //Clear Channel 5 Transfer Complete flag
DMA1_Channel5->CCR &= ~DMA_CCR_EN;
DMA1_Channel5->CNDTR = sizeof(stringtoreceive);/* Data size */
DMA1_Channel5->CCR |= DMA_CCR_EN;
}
}
void EXTI4_15_IRQHandler(void){
if(!(GPIOC->IDR & (1 << 13))){
/* Clear EXTI 13 flag */
EXTI->PR = EXTI_PR_PIF13;
/* start 8-bit transmission with DMA */
DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
}
}
//void EXTI4_15_IRQHandler(void){
// if((EXTI->PR & EXTI_PR_PIF13) == EXTI_PR_PIF13){
// /* Clear EXTI 13 flag */
// EXTI->PR = EXTI_PR_PIF13;
//
// /* start 8-bit transmission with DMA */
// DMA1_Channel4->CCR &= ~DMA_CCR_EN; //channel disable
// DMA1_Channel4->CNDTR = sizeof(stringtosend);/* Data size */
// DMA1_Channel4->CCR |= DMA_CCR_EN; //channel enable
// }
//}
Now then, this specific code is based on an example from the STM32L0 snippets package 1.20, USART/Communcation Using DMA. USART 1 was simply redefined to USART 2 (as that is the one used by the virtual COM port), and the DMA channels were redefined according to that as well. However, the problem here is very simple: it will only print stringtosend once (would like to do it every time button B1 is pressed), and will not receive data by RX either - as if it completely ignores the DMA interrupt handler - which I am not sure how to test (no trace features available on this board). What I have seems to reflect the reference manual well enough, and all the main does is:
int main(){
SystemCoreClockInit();
CONFIGURE_UART_PARAM();
ENABLE_UART_DMA();
pushbutton_def();
CONFIGURE_EXTI();
while(1){
}
...which should just react to the defined interrupts, however it does not, and for the life of me, I cannot see why. I would love if you could help me - I would also like to avoid HAL or LL APIs - this is not a complex enough project to warrant their usage (several inputs, outputs, comms between two boards by USART/DMA), plus I would prefer to learn working closer to the register level.
Thanks!
edit (in response to Berendi's suggestions):
1. GPIOC was defined in another file, called with pushbutton_def():
RCC->IOPENR |= (1UL << 2); //enable GPIOC
I understand exactly what you mean by your explanation (indeed, the register referred by those two is "the same", 0x00000020U), but I am not sure as to how to redefine it: here is my attempt after looking at the reference manual (SYSCFG part) and the source (still, it does not work):
SYSCFG->EXTICR[3] = ((SYSCFG->EXTICR[3] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC);
As suggested, I have added USART2->ICR = USART_ICR_TCCF; to the EXTIhandler, right after the DMA channels. I have kept it in the USART definition. The message is still only being sent once, though.
GPIOC is not enabled
Here,
RCC->IOPENR |= ( 1ul << 0); //Enable GPIOA clock
you should enable GPIOC too.
EXTI13 is mapped to PA13
Here,
SYSCFG->EXTICR[0] = ((SYSCFG->EXTICR[0] & 0x0000) | SYSCFG_EXTICR4_EXTI13_PC); //clear EXTICR and set to PC13(B1)
you are setting the configuration register for EXTI0-EXTI3, actually mapping EXTI1 to PC1. EXTI13 remains mapped to PA13, which is actually SWDIO, connected to the onboard debugger. I guess the traffic on SWDIO triggers the EXTI interrupt, the handler checks PC13 which is always reading 0 because the port is disabled, and enables DMA. DMA transmit works only once though, because
USART_ISR_TC is not cleared in the interrupt
but only once at startup. You should move this line
USART2->ICR = USART_ICR_TCCF;
to the EXTI interrupt handler.
I'm not sure why receiving doesn't work, perhaps the DMA handler has no chance to run, because EXTI is constantly retriggered by SWD traffic. Both interrupts have the same priority, the one with lower interrupt number wins, which is the EXTI handler. If it's always retriggered before it finishes, then it will be called again, not letting the other handler to run.

I2C interface on Tiva

On a Tiva (Texas Instruments Cortex M4F ARM) TM4C129XNCZAD I have a problem with I2C interface. I have enabled both a master on I2C module 4 thru port K and a slave on I2C module 6 thru port B. I have interconnected both I2C modules. Using Texas Instruments driver library I tried to send 1 byte using I2C_MASTER_CMD_SINGLE_SEND command. I spend a lot of time to make it working, but SCK line keeps Low logical level. I followed exactly TivaWare™ Peripheral Driver Library USER’S GUIDE, but the communication doesn't work. Has anybody some experience with it?
There is my code:
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "inc/tm4c129xnczad.h"
#define SLAVE_ADDRESS 0x3C
void delay (void)
{
volatile uint32_t ui32Loop;
for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++);
}
volatile uint32_t result;
int main (void)
{
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
//
// Enable the GPIO port that is used for the on-board LED.
//
SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R3 | SYSCTL_RCGCGPIO_R9 | SYSCTL_RCGCGPIO_R1;
//
// Do a dummy read to insert a few cycles after enabling the peripheral.
//
result = SYSCTL_RCGCGPIO_R;
//
// Enable the GPIO pin for the LED (PD3). Set the direction as output, and
// enable the GPIO pin for digital function.
//
GPIO_PORTD_AHB_DIR_R = 0x8;
GPIO_PORTD_AHB_DEN_R = 0x8;
GPIO_PORTK_DEN_R = 0xC0; // Enable Port K for I2C module 4
GPIO_PORTB_AHB_DEN_R = 0xC0; // Enable Port B for I2C module 6
SYSCTL_RCGCI2C_R = (1 << 4) | (1 << 6); // Mode Clock Gating Control for I2C modules 4 and 6
GPIO_PORTK_AFSEL_R = 0xC0; // Alternate Function Select PK6, PK7
GPIO_PORTB_AHB_AFSEL_R = 0xC0; // Alternate Function Select PB6, PB7
GPIOPinConfigure(GPIO_PK6_I2C4SCL);
GPIOPinConfigure(GPIO_PK7_I2C4SDA);
GPIOPinConfigure(GPIO_PB6_I2C6SCL);
GPIOPinConfigure(GPIO_PB7_I2C6SDA);
GPIOPinTypeI2C(GPIO_PORTK_BASE, 7); // Configurtes SDA
GPIOPinTypeI2CSCL(GPIO_PORTK_BASE, 6); // Configurtes SCL
GPIOPinTypeI2C(GPIO_PORTB_BASE, 7); // Configurtes SDA
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, 6); // Configurtes SCL
I2CMasterInitExpClk(I2C4_BASE, SysCtlClockGet(), false);
I2CSlaveEnable(I2C6_BASE);
I2CSlaveInit(I2C6_BASE, SLAVE_ADDRESS);
I2CMasterSlaveAddrSet(I2C4_BASE, SLAVE_ADDRESS, false);
//
// Loop forever.
//
while(1)
{
//
// Turn on the LED.
//
GPIO_PORTD_AHB_DATA_R |= 0x8;
I2CMasterDataPut(I2C4_BASE, 0x33);
I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_SINGLE_SEND);
//
// Wait until the slave has received and acknowledged the data.
//
while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_RREQ));
//
// Read the data from the slave.
//
result = I2CSlaveDataGet(I2C6_BASE);
//
// Wait until master module is done transferring.
//
while(I2CMasterBusy(I2C4_BASE));
//
// Delay for a bit.
//
delay ();
//
// Turn off the LED.
//
GPIO_PORTD_AHB_DATA_R &= ~(0x8);
//
// Delay for a bit.
//
delay ();
}
}
The problem has been resolved. There were issues:
It is essential to populate external pull ups.
Use GPIOPinTypeI2C() 2nd parameter as a bit field instead of a bit number.
The procedure SysCtlClockSet() is dedicated specially to TM4C123 devices. Instead use g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320), 40000000);
For a Master clock setting do not use SysCtlClockGet() procedure. This is also dedicated to TM4C123 devices. Instead use I2CMasterInitExpClk(I2C4_BASE, g_ui32SysClock, false);
Here is the updated code,
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "inc/tm4c129xnczad.h"
#define SLAVE_ADDRESS 0x3C
void delay (void)
{
volatile uint32_t ui32Loop;
for(ui32Loop = 0; ui32Loop < 200; ui32Loop++);
}
volatile uint32_t result;
uint32_t g_ui32SysClock;
int main(void)
{
g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_OSC_MAIN | SYSCTL_XTAL_25MHZ | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320), 40000000);
//
// Enable the GPIO port that is used for the on-board LED.
//
SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R3 | SYSCTL_RCGCGPIO_R9 | SYSCTL_RCGCGPIO_R1;
//
// Do a dummy read to insert a few cycles after enabling the peripheral.
//
result = SYSCTL_RCGCGPIO_R;
//
// Enable the GPIO pin for the LED (PD3). Set the direction as output, and
// enable the GPIO pin for digital function.
//
GPIO_PORTD_AHB_DIR_R = 0x8;
GPIO_PORTD_AHB_DEN_R = 0x8;
SYSCTL_RCGCI2C_R = (1 << 4) | (1 << 6); // Mode Clock Gating Control for I2C modules 4 and 6
GPIOPinConfigure(GPIO_PK6_I2C4SCL);
GPIOPinConfigure(GPIO_PK7_I2C4SDA);
GPIOPinConfigure(GPIO_PB6_I2C6SCL);
GPIOPinConfigure(GPIO_PB7_I2C6SDA);
GPIOPinTypeI2C(GPIO_PORTK_BASE, (1 << 7)); // Configures SDA
GPIOPinTypeI2CSCL(GPIO_PORTK_BASE, (1 << 6)); // Configures SCL
GPIOPinTypeI2C(GPIO_PORTB_BASE, (1 << 7)); // Configures SDA
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, (1 << 6)); // Configures SCL
I2CMasterInitExpClk(I2C4_BASE, g_ui32SysClock, false);
I2CSlaveEnable(I2C6_BASE);
I2CSlaveInit(I2C6_BASE, SLAVE_ADDRESS);
I2CMasterSlaveAddrSet(I2C4_BASE, SLAVE_ADDRESS, false);
//
// Loop forever.
//
while(1)
{
//
// Turn on the LED.
//
GPIO_PORTD_AHB_DATA_R |= 0x8;
I2CMasterDataPut(I2C4_BASE, 0x33);
I2CMasterControl(I2C4_BASE, I2C_MASTER_CMD_SINGLE_SEND);
//
// Wait until the slave has received and acknowledged the data.
//
while(!(I2CSlaveStatus(I2C6_BASE) & I2C_SLAVE_ACT_RREQ));
//
// Read the data from the slave.
//
result = I2CSlaveDataGet(I2C6_BASE);
//
// Wait until master module is done transferring.
//
while(I2CMasterBusy(I2C4_BASE));
//
// Delay for a bit.
//
delay ();
//
// Turn off the LED.
//
GPIO_PORTD_AHB_DATA_R &= ~(0x8);
//
// Delay for a bit.
//
delay ();
}
}
I was able to get I2C working on TIVA Launchpad TM4C123GH6PM. I used the the Tivaware library. I created 3 functions, init and write. Here are the functions.
Initialization
void initI2C0(void)
{
SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
//reset I2C module
SysCtlPeripheralReset(SYSCTL_PERIPH_I2C0);
//enable GPIO peripheral that contains I2C
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
// Configure the pin muxing for I2C0 functions on port B2 and B3.
GPIOPinConfigure(GPIO_PB2_I2C0SCL);
GPIOPinConfigure(GPIO_PB3_I2C0SDA);
// Select the I2C function for these pins.
GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);
GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
// Enable and initialize the I2C0 master module. Use the system clock for
// the I2C0 module. The last parameter sets the I2C data transfer rate.
// If false the data rate is set to 100kbps and if true the data rate will
// be set to 400kbps.
I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
//clear I2C FIFOs
HWREG(I2C0_BASE + I2C_O_FIFOCTL) = 80008000;
}
I2C Write Function
void writeI2C0(uint16_t device_address, uint16_t device_register, uint8_t device_data)
{
//specify that we want to communicate to device address with an intended write to bus
I2CMasterSlaveAddrSet(I2C0_BASE, device_address, false);
//register to be read
I2CMasterDataPut(I2C0_BASE, device_register);
//send control byte and register address byte to slave device
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_SEND_START);
//wait for MCU to finish transaction
while(I2CMasterBusy(I2C0_BASE));
I2CMasterSlaveAddrSet(I2C0_BASE, device_address, true);
//specify data to be written to the above mentioned device_register
I2CMasterDataPut(I2C0_BASE, device_data);
//wait while checking for MCU to complete the transaction
I2CMasterControl(I2C0_BASE, I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
//wait for MCU & device to complete transaction
while(I2CMasterBusy(I2C0_BASE));
}
Complete code for TIVA + I2C can be found here.
Reference
I2C Communication with the TI Tiva TM4C123GXL

Resources