I2C Not working with PCA9685 - c

I'm using an Atmega168 I'm attempting to use I2C with my PCA9685 Servo driver.
I'm using this I2C library: https://github.com/g4lvanix/I2C-master-lib
I'm attempting to start the I2C connection with my PCA9685 (Address: 0x41).
For some reason the I2C Library is bouncing back an error because the acknowledge bit isn't being sent. What is wrong here? My SDA and SCL pins are hooked up to 10k pull up resistors, and they are connected to the PCA9685 correctly. Yet it still isn't working. Could it be my PCA9685 chip? I also know the address is 0x41 because I manually bridged an address connection to assign that address.
Here is my code:
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include "i2c.h"
#define SERVO_MIN 1000
#define SERVO_MAX 2000
#define SERVO_MID 1500
#define PCA9685_ADDR 0x40
#define PCA9685_MODE1 0x0
#define LED0_ON_L 0x6
#define LED0_ON_H 0x7
#define LED0_OFF_L 0x8
#define LED0_OFF_H 0x9
#define LED PB0
#define LED_DDR DDRB
#define LED_PORT PORTB
#define DELAYTIME 200
#define setBit(sfr, bit) (_SFR_BYTE(sfr) |= (1 << bit))
#define clearBit(sfr, bit) (_SFR_BYTE(sfr) &= ~(1 << bit))
#define toggleBit(sfr, bit) (_SFR_BYTE(sfr) ^= (1 << bit))
void setupController();
void setServo(uint8_t id, uint8_t start, uint8_t stop);
int main(void)
{
setBit(LED_DDR, LED);
clock_prescale_set(clock_div_1);
i2c_init();
uint8_t err = i2c_start(0x41);
while(err == 1) {
setBit(LED_PORT, LED);
uint8_t err = i2c_start(0x41);
}
clearBit(LED_PORT, LED);
while(1) {
}
return 0;
}
Thank you in advance!

Related

toggling stm32 led using structure types

hello i am new with the stm32 board i am trying to toggle the user led that is connected to port A pin 5 (nucleo l476rg board) , I wrote this program from scratch using structure types but unfortunately the led is not blinking … I double checked all the addresses because i tried it without the structures and it worked , can any one tell me what’s the problem here ? here's the code:
// Port A
// pin 5
#include <stdint.h>
#define periph_base (0x40000000UL)
#define ahb2_offset (0x08000000UL)
#define ahb2_base (periph_base + ahb2_offset)
#define ahb1_offset (0x20000UL)
#define ahb1_base (periph_base + ahb1_offset)
#define gpioa_offset (0x000000UL)
#define gpioa_base (ahb2_base + gpioa_offset)
#define rcc_offset (0X1000UL)
#define rcc_base (ahb1_base + rcc_offset)
#define rcc_ahb2enr_offset (0x4CUL)
#define rcc_ahb2enr (*(volatile unsigned int *)(rcc_base +rcc_ahb2enr_offset))
#define gpioa_moder_offset (0x00UL)
#define gpioa_moder (*(volatile unsigned int*)(gpioa_base +gpioa_moder_offset))
#define odr_offset (0X14UL)
#define odr (*(volatile unsigned int*)(gpioa_base + odr_offset))
#define gpioa_en (1U<<0)
#define pin5 (1U<<5)
#define led_pin pin5
typedef struct
{
volatile uint32_t MODER;
volatile uint32_t DUMMY[4];
volatile uint32_t ODR;
}gpio_Typedef;
typedef struct
{
volatile uint32_t DUMMY[16];
volatile uint32_t AHB2ENR;
}rcc_Typedef;
#define RCC ((rcc_Typedef*) rcc_base)
#define GPIOA ((gpio_Typedef*) gpioa_base)
int main(void)
{
RCC->AHB2ENR |= gpioa_en;
GPIOA->MODER |= (1U<<10);
GPIOA->MODER &=~ (1U<<11);
while(1)
{
GPIOA->ODR ^= led_pin;
for(long int i =0; i<100000;i++){}
}
}
It appears that i had the wrong reference manual , in the rcc_Typedef Structure the dummy array should have 19 as size not 16 , there are 19 registers before the rcc_ahb2enr .

simple arduino code without using avr headers in avr-libc

I have this simple code
That I am trying to compile for arduino microcontroller and without using avr headers. I just defined all macros in my source program file
but my gcc-avr says
led.c:15:8: error: lvalue required as left operand of assignment
DDRB |= 0B100000; // PORTB5 1010
^
Now I can expect this error on some cpu that io area is not virtual memory space of this process but I am running my code on mocrocontroller that must have execution bit. how to get rid of this message and compile it and able to run on arduino
But the gcc-avr throws error that
#define F_CPU 16000000
#define BLINK_DELAY_MS 5000
#include <util/delay.h>
#define __SFR_OFFSET 0x20
#define _SFR_IO8(io_addr) ((io_addr) + __SFR_OFFSET)
#define DDRB _SFR_IO8(0x04)
#define PORTB _SFR_IO8(0x05)
int main (void)
{
// Arduino digital pin 13 (pin 5 of PORTB) for output
DDRB |= 0B100000; // PORTB5 1010
while(1) {
// turn LED on
PORTB |= 0B100000; // PORTB5
// _delay_ms(BLINK_DELAY_MS);
int x=0;
while(x<25)
{
x++;
}
x=0;
// turn LED off
PORTB &= ~ 0B100000; // PORTB5
//hello
while(x<25)
{
x++;
}
//_delay_ms(BLINK_DELAY_MS);
}
}
The problems are the macros, you are defining the register as an integer, not as an address to an integer.
DDRB expands to 0x04 + 0x20 so you end up with code like (0x04 + 0x20) |= 0B100000;. You should be able to fix this with a cast and then de-reference:
#define _SFR_IO8(io_addr) ( *(volatile uint8_t*) ((io_addr) + __SFR_OFFSET) )
For details see How to access a hardware register from firmware?
Please also note that macros starting with double underscore __ are reserved for the compiler, so we should never use that or we might end up with naming collisions.

Why don't I get HIGH on output pins of MCP23S17?

I use a STM32 connected with SPI to a MCP23S17 16 bit I/O port expander.
I want to make all 16 I/0 pins as output and just making them all LOW or HIGH.
I used an oscilloscope to check is SPI transmit the signals right, and it does.
The only thing is that on the I/O pins I get around 0.4V. Not 5V.
Sometimes I get a value of 1.4V but it goes away, weird thing...
Can somebody check my code and tell me where is my mistake? I am pretty sure I did the code wrong somewhere.
MCP23S17.h:
#ifndef INC_MCP23S17_H_
#define INC_MCP23S17_H_
#include "stm32f1xx_hal_conf.h"
extern SPI_HandleTypeDef hspi2;
void MCP23S17_SPI_Write(uint8_t reg_addr, uint8_t data);
uint8_t MCP23S17_SPI_Read(uint8_t data);
#define CS_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
#define CS_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
#define MCP23S17_AAA ((uint8_t)0x0E)
#define MCP23S17_ADDRESS ((uint8_t)0x40)
#define MCP23S17_W ((uint8_t)0x00)
#define MCP23S17_R ((uint8_t)0x01)
#define MCP23S17_IODIRA ((uint8_t)0x00)
#define MCP23S17_IPOLA ((uint8_t)0x02)
#define MCP23S17_GPINTENA ((uint8_t)0x04)
#define MCP23S17_DEFVALA ((uint8_t)0x06)
#define MCP23S17_INTCONA ((uint8_t)0x08)
#define MCP23S17_IOCONA ((uint8_t)0x0A)
#define MCP23S17_GPPUA ((uint8_t)0x0C)
#define MCP23S17_INTFA ((uint8_t)0x0E)
#define MCP23S17_INTCAPA ((uint8_t)0x10)
#define MCP23S17_GPIOA ((uint8_t)0x12)
#define MCP23S17_OLATA ((uint8_t)0x14)
#define MCP23S17_IODIRB ((uint8_t)0x01)
#define MCP23S17_IPOLB ((uint8_t)0x03)
#define MCP23S17_GPINTENB ((uint8_t)0x05)
#define MCP23S17_DEFVALB ((uint8_t)0x07)
#define MCP23S17_INTCONB ((uint8_t)0x09)
//#define MCP23S17_IOCONB ((uint8_t)0x0B)
#define MCP23S17_GPPUB ((uint8_t)0x0D)
#define MCP23S17_INTFB ((uint8_t)0x0F)
#define MCP23S17_INTCAPB ((uint8_t)0x11)
#define MCP23S17_GPIOB ((uint8_t)0x13)
#define MCP23S17_OLATB ((uint8_t)0x15)
//#define MCP23S17_INT_ERR ((uint8_t)255)
//#define BANK ((uint8_t)0x80) //bit 7 of IOCON
//#define MIRROR ((uint8_t)0x40) //bit 6 of IOCON
//#define SEQOP ((uint8_t)0x20) //bit 5 of IOCON
//#define DISSLW ((uint8_t)0x10) //bit 4 of IOCON
//#define HAEN ((uint8_t)0x08) //bit 3 of IOCON
//#define ODR ((uint8_t)0x04) //bit 2 of IOCON
//#define INTPOL ((uint8_t)0x02) //bit 1 of IOCON
//#define unused ((uint8_t)0x00) //bit 0 of IOCON
//-----------------------
#define MCP23S17_MODERA_W(x) MCP23S17_SPI_Write(MCP23S17_IODIRA, ((uint8_t)x))
#define MCP23S17_MODERA_R() MCP23S17_SPI_Read(MCP23S17_IODIRA)
#define MCP23S17_IPOLA_W(x) MCP23S17_SPI_Write(MCP23S17_IPOLA, ((uint8_t)x))
#define MCP23S17_ODRA_W(x) MCP23S17_SPI_Write(MCP23S17_OLATA, ((uint8_t)x))
#define MCP23S17_ODRA_R() MCP23S17_SPI_Read(MCP23S17_OLATA)
#define MCP23S17_IDRA_R() MCP23S17_SPI_Read(MCP23S17_GPIOA) // Reflect the value of the port A
#define MCP23S17_PUDA_W(x) MCP23S17_SPI_Write(MCP23S17_GPPUA, ((uint8_t)x))
#define MCP23S17_PUDA_R() MCP23S17_SPI_Read(MCP23S17_GPPUA)
#define MCP23S17_SETUP_W(x) MCP23S17_SPI_Write(MCP23S17_IOCONA, ((uint8_t)x))
#define MCP23S17_SETUP_R() MCP23S17_SPI_Read(MCP23S17_IOCONA))
#define MCP23S17_MODERB_W(x) MCP23S17_SPI_Write(MCP23S17_IODIRB, ((uint8_t)x))
#define MCP23S17_MODERB_R() MCP23S17_SPI_Read(MCP23S17_IODIRB)
#define MCP23S17_IPOLB_W(x) MCP23S17_SPI_Write(MCP23S17_IPOLB, ((uint8_t)x))
#define MCP23S17_ODRB_W(x) MCP23S17_SPI_Write(MCP23S17_OLATB, ((uint8_t)x))
#define MCP23S17_ODRB_R() MCP23S17_SPI_Read(MCP23S17_OLATB)
#define MCP23S17_IDRB_R() MCP23S17_SPI_Read(MCP23S17_GPIOB) // Reflect the value of the port B
#define MCP23S17_PUDB_W(x) MCP23S17_SPI_Write(MCP23S17_GPPUB, ((uint8_t)x))
#define MCP23S17_PUDB_R() MCP23S17_SPI_Read(MCP23S17_GPPUB)
//-----------------------
void GPIO_Write_Pins(uint16_t);
void GPIO_Expander_Init();
void MCP23S17_SPI_Write(uint8_t reg_addr, uint8_t data);
#endif /* INC_MCP23S17_H_ */
MCP23S17.c
#include "main.h"
#include "MCP23S17.h"
void MCP23S17_SPI_Write(uint8_t reg_addr, uint8_t data)
{
uint8_t pBuff[3];
pBuff[0] = MCP23S17_ADDRESS|MCP23S17_AAA|MCP23S17_W; //optocode that contain 0100 + 111 + 0 (read command)
pBuff[1] = reg_addr; // register address
pBuff[2] = data; // the value that is modified on the register, check datasheet
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
HAL_SPI_Transmit_IT(&hspi2, pBuff, 3); //transmit on the spi2 the optocode, register adress and the value for the register
for(uint32_t i = 0; i < 85; i++) //delay
{}
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
}
void GPIO_Expander_Init()
{
MCP23S17_SETUP_W(0x00);
MCP23S17_MODERA_W(0x00);
MCP23S17_MODERB_W(0x00);
MCP23S17_PUDA_W(0x00);
MCP23S17_PUDB_W(0x00);
//MCP23S17_IPOLA_W(0x00);
//MCP23S17_IPOLB_W(0x00);
}
void GPIO_Write_Pins(uint16_t dataspi)
{
//16 to 8 and 8
uint8_t data_half[2];
data_half[0]=*((uint8_t*)&(dataspi)+1); //split the first half of the data
data_half[1]=*((uint8_t*)&(dataspi)+0); //split the second half of the data
MCP23S17_ODRA_W(data_half[0]); //first half
MCP23S17_ODRB_W(data_half[1]); //second half
}
and in main.c
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET);
GPIO_Expander_Init();
GPIO_Write_Pins(0xFFFF);
while(1)
{
}
The only thing is that on the I/O pins I get around 0.4V
This is likely because GPIOs are not initialized.
You also have a different problem to deal with.
for(uint32_t i = 0; i < 85; i++) //delay
{}
This loop is very likely to be optimize away by the compiler. This is because, it doesn't do anything.
There are more sophisticated ways of generating delays in a microcontroller. The best possible solution would be to use a timer module.
So coming back with the solution, the code was good, 2 first gpio expanders that I tried were purely burned. For a much shorter working code just comment here asking for it. Thank everyone for advices cause I took them in consideration in the final form of code.

Trying to interface Oled display with MLX90614 IR Sensor

Hi i am working on a school project where I'm supposed to output temperature values on an Oled display.
i have managed to do it with Arduino Code so i know that the sensor and display works, but the code has to be written in Atmel studio in pure embedded C for the project.
I'm not sure what I'm doing wrong and what I'm am doing right.
I'm struggling with C code and the I2C protocol.
i have done a lot of research on the subject but evertime i seem to only find arduino tutorials, or theory related posts.
I'm totally new to programming.
anyway this is the code i have come up with so far.
the txt i have written for the display is displayed. everthing works as it should except for the temperatur values:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "OLED.h"
#include "ATmega_Init.h"
#include "i2c_master.h"
//From Arduino Adafruit_MLX90614 lib
// RAM
#define MLX90614_RAWIR1 0x04
#define MLX90614_RAWIR2 0x05 //Important for reading out
#define MLX90614_TA 0x06
#define MLX90614_TOBJ1 0x07
#define MLX90614_TOBJ2 0x08
// EEPROM
#define MLX90614_TOMAX 0x20
#define MLX90614_TOMIN 0x21
#define MLX90614_PWMCTRL 0x22
#define MLX90614_TARANGE 0x23
#define MLX90614_EMISS 0x24
#define MLX90614_CONFIG 0x25
#define MLX90614_ADDR 0x0E
#define MLX90614_ID1 0x3C
#define MLX90614_ID2 0x3D
#define MLX90614_ID3 0x3E
#define MLX90614_ID4 0x3F
#define SLAVE_ADDRESS 0x5A
int main(void)
{
//uint8_t test_recieve[2];
uint16_t test_recieve_high; //To receive the high byte
uint16_t test_recieve_low; //To receive the low byte
init_atmega_setup();
initDisplay();
i2c_init();
clsDisplay();
strFont5XY("Floor Unit",25,3);
_delay_ms(2000);
clsDisplay();
//uint8_t receive_command = 0x00; //Needs the receive command,
definitely not just 0x00
while(1){
i2c_start(SLAVE_ADDRESS);
i2c_write(MLX90614_TOBJ1);
i2c_start(SLAVE_ADDRESS);
test_recieve_high = i2c_read_ack();
i2c_start(MLX90614_TOBJ1);
test_recieve_high = ((uint8_t)i2c_read_ack())<<8;
test_recieve_high |= i2c_read_ack();
i2c_stop();
i2c_start(SLAVE_ADDRESS);
i2c_write(MLX90614_TOBJ1);*/
i2c_start(MLX90614_TOBJ1);
test_recieve_low = ((uint8_t)i2c_read_ack())<<8;
test_recieve_low |= i2c_read_ack();
i2c_stop();
intFont5XY(test_recieve_low,1,0);
intFont5XY(test_recieve_high,1,1);
_delay_ms(1000);
};
};

AVR clean pin aliasing solution - enumerating I/O bits

I'm working in C on an Arduino device, where the pins are labeled differently. I'm using PLAIN C though, not the Arduino "language".
Each pin is defined by it's port (eg. PORTB) and pin (bit) in the port (eg. PB0).
I'd like to concisely alias pins, so I could make macros or functions somewhat like what the Arduino uses:
pin_direction(D2, 1); // D2 as output
set_pin(D2, 0); // write zero to D2
pin_direction(D3, 0); // D3 as input
enable_pullup(D3, 1); // enable D3 pullup
Instead (atm) I have to use something ugly like this:
#define D0 0
#define D1 1
#define D2 2
#define D3 3
...
#define D10 2
#define D11 3
#define PORT_D0 PORTD
#define PORT_D1 PORTD
#define PORT_D2 PORTD
#define PORT_D3 PORTD
...
#define PORT_D10 PORTB
#define PORT_D11 PORTB
// the same for PIN_xx and DDR_xx
And then I can use macros to do the work:
#define sbi(port, bit) (port) |= _BV(bit)
#define cbi(port, bit) (port) &= ~ _BV(bit)
sbi(DDR_D2, D2); // D2 to output
cbi(PORT_D2, D2); // D2 to output
sbi(DDR_D3, D3); // D3 as input
sbi(PORT_D3, D3); // D3 pullup enable
Now this works, but it's very messy. Any idea how to - without a monster overhead of something like huge switch - do this better - more like my first example? Somehow enumerate all bits and then resolve the right register on the fly?
I'm using avr-gcc with avr-libc.
You can define macros which expand to multiple tokens, e.g.
#define PIN_D0 PORTD, 0
#define PIN_D1 PORTD, 1
...
#define PIN_D10 PORTB, 2
Then use them in macros such as:
sbi(PIN_D0); // expands to sbi(PORTD, 0)
Some utility macros that may be useful with these macros are:
#define PORT_OF(port, bit) port
#define BIT_OF(port, bit) bit
which can be used in contexts such as:
PORT_OF(PIN_D10) // expands to PORTB
BIT_OF(PIN_D10) // expands to 2
Here's the solution I use.
In my util.h (common to all my AVR projects):
#define DDR_REG(port) DDR ## port
#define PORT_REG(port) PORT ## port
#define PIN_REG(port) PIN ## port
#define SET_BIT(port, bit) do { (port) |= (1 << (bit)); } while(0)
#define CLR_BIT(port, bit) do { (port) &= ~(1 << (bit)); } while(0)
#define BIT_IS_SET(port, bit) ((((uint8_t)(port)) >> ((uint8_t)(bit))) & 0x1)
#define IO_SET_INPUT_AUX(port, bit) CLR_BIT(DDR_REG(port), bit)
#define IO_SET_AS_INPUT(io) IO_SET_INPUT_AUX(io)
#define IO_SET_OUTPUT_AUX(port, bit) SET_BIT(DDR_REG(port), bit)
#define IO_SET_AS_OUTPUT(io) IO_SET_OUTPUT_AUX(io)
#define IO_OUTPUT_0_AUX(port, bit) CLR_BIT(PORT_REG(port), bit)
#define IO_OUTPUT_0(io) IO_OUTPUT_0_AUX(io)
#define IO_OUTPUT_1_AUX(port, bit) SET_BIT(PORT_REG(port), bit)
#define IO_OUTPUT_1(io) IO_OUTPUT_1_AUX(io)
#define IO_GET_INPUT_AUX(port, bit) BIT_IS_SET(PIN_REG(port), bit)
#define IO_GET_INPUT(io) IO_GET_INPUT_AUX(io)
In my pin mappings file:
#define UPBTN_IO B,7
#define DOWNBTN_IO D,0
#define ENTERBTN_IO D,1
(etc)
In code:
IO_SET_AS_INPUT(UPBTN_IO);
This relies on some fun preprocessor bits, like there only being one round of macro expansion on macro parameters.

Resources