Using pigpio with MCP23017 for interrupt reading - c++

I'm trying to read interrupt changes from a MCP23017 board using pigpio but i'm not getting any notification... what seems that i do not know is the initialize procedures that i need to do (looking into the documentation http://ww1.microchip.com/downloads/en/devicedoc/20001952c.pdf it seems that i'm not using the correct values...).
I'm using B side for inputs and A for outputs:
i2cWriteByteData(i2cHandle, IODIRB, 0xFF); // inputs
i2cWriteByteData(i2cHandle, IODIRA, 0x00); // outputs
then i try to set the interrupt state (that it seems to be wrong), but i'm copied from 'I2C Sonar' example from http://abyz.me.uk/rpi/pigpio/examples.html
i2cWriteByteData(i2cHandle, GPINTENB, 0x00); // Disable.
i2cWriteByteData(i2cHandle, DEFVALB, 0x00) ; // N/A.
i2cWriteByteData(i2cHandle, INTCONB, 0x00) ; // On change.
and then registe the handler to read the changes:
gpioSetMode(ALERTPIN, PI_INPUT); /* start capacitor recharge */
gpioSetISRFuncEx(ALERTPIN, EITHER_EDGE, 0, alert, nullptr); /* call alert when state changes */
So my question is what I'm missing that my alert function is never called when states change (strangely it is called when i do a dump on the console (sudo i2cdump -y 1 0x20)
Other question is are my defines correct? why are two sets of values (Address IOCON.BANK = 1 and 0)? what are the addresses for MCP23017? (i'm using Address IOCON.BANK = 0):
#define IODIRA 0x00
#define IODIRB 0x01
#define IPOLA 0x02
#define IPOLB 0x03
#define GPINTENA 0x04
#define GPINTENB 0x05
#define DEFVALA 0x06
#define DEFVALB 0x07
#define INTCONA 0x08
#define INTCONB 0x09
#define IOCONA 0x0A
#define IOCONB 0x0B
#define GPPUA 0x0C
#define GPPUB 0x0D
#define INTFA 0x0E
#define INTFB 0x0F
#define INTCAPA 0x10
#define INTCAPB 0x11
#define GPIOA 0x12
#define GPIOB 0x13
#define OLATA 0x14
#define OLATB 0x15
#define ALERTPIN 26
Update:
Next i show all the code that I perform and after read the documentation I think that i'm doing everything correct, but it seems that i'm missing some initialization... and strangely the GPIOA read when input button connected to GPA0 is pressed the value is 255 (0xff) and if is not press then is 0... strange it should be 1 when pressed right?
void estimulateMCP23017(unsigned i2cAddr=0x20,unsigned i2cBus=1){
//open i2c MCP23017
int i2cHandle = i2cOpen(i2cBus, i2cAddr, 0);
//open is with success if is >=0
if(i2cHandle>=0){
//IODIR: I/O DIRECTION REGISTER (ADDR 0x00)
//1 = Pin is configured as an input.
//0 = Pin is configured as an output
//we can set each separately if we want
//set side A as output (0)
i2cWriteByteData(i2cHandle, IODIRA, 0xFF); // A is inputs
//set side B as input (1)
i2cWriteByteData(i2cHandle, IODIRB, 0x00); // B is outputs
//turn all outputs to on (pins 0 to 7 is the bit high in 0x00-0xFF)
i2cWriteByteData(i2cHandle, OLATA, 0x00);
i2cWriteByteData(i2cHandle, OLATB, 0xFF);
//now listen for changes on side (B)
// General purpose I/O interrupt-on-change bits <7:0>
//1 = Enables GPIO input pin for interrupt-on-change event.
//0 = Disables GPIO input pin for interrupt-on-change event.
i2cWriteByteData(i2cHandle, GPINTENA, 0xFF); // Enable all
i2cWriteByteData(i2cHandle, GPINTENB, 0x00); // disable
//Sets the compare value for pins configured for interrupt-on-change from defaults <7:0>
//If the associated pin level is the opposite from the register bit, an interrupt occurs
i2cWriteByteData(i2cHandle, DEFVALA, 0x00); // does not matter
i2cWriteByteData(i2cHandle, DEFVALB, 0x00); // does not matter
// Controls how the associated pin value is compared for interrupt-on-change <7:0>
//1 = Pin value is compared against the associated bit in the DEFVAL register.
//0 = Pin value is compared against the previous pin value.
i2cWriteByteData(i2cHandle, INTCONA, 0x00) ; // On change.
i2cWriteByteData(i2cHandle, INTCONB, 0x00) ; // does not matter.
cout << "waiting for changes...(input value: "<<i2cReadByteData(i2cHandle, GPIOA)<<")\n";
gpioSetMode(ALERTPIN, PI_INPUT); /* set alert pin as input */
gpioSetISRFuncEx(ALERTPIN, EITHER_EDGE, 0, alert, nullptr); /* call alert when state changes */
cin.get();
i2cClose(i2cHandle);
}
}

Since Bank A is your input, you should use:
i2cWriteByteData(i2cHandle, GPINTENA, 0xFF); // Enable.
i2cWriteByteData(i2cHandle, INTCONA, 0x00) ; // On change.
Your defines look right. The MCP23017 has two different modes of addressing the registers. The mode is set using the IOCON.BANK flag. Just never touch that bit, there's no reason to do so.

Related

Get pin input without the Arduino Library

I am not allowed to use the Arduino Library (or any Library) for this program. How would I check the input of a pin?
I found two different functions:
In Arduino.h:
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
Following digitalRead back to pgmspace.h:
#define __LPM_enhanced__(addr) \
(__extension__({ \
uint16_t __addr16 = (uint16_t)(addr); \
uint8_t __result; \
__asm__ __volatile__ \
( \
"lpm %0, Z" "\n\t" \
: "=r" (__result) \
: "z" (__addr16) \
); \
__result; \
}))
For the first one, I don't know where bit and value come from and I just don't understand the second one at all.
There is no need to go to these implementations. It pretty simple as follows.
LED13 will turn on when Pin 0 is high. I tested this code on arduino
#include <avr/io.h> // Includes all the definition of register port etc
#ifndef F_CPU
#define F_CPU 16000000UL //Need to include it before <util/delay.h>
#endif //Change 16000000 with your crystal freq. In my case its 16 MHz
#include <util/delay.h> //includes delay functions delay_ms and delay_us
void setup() {
// put your setup code here, to run once:
DDRB |= 0xFF; //Configured Port B as OP
DDRD &= 0x00; //Configured Port D as IP
}
void loop() {
// put your main code here, to run repeatedly:
if (PIND&(0x01)) //to check pin0 of portD (which is Pin 0 of arduino)
PORTB |= 0xFF;
else
PORTB &= 0x00;
}
I will assume that you use Arduino Uno, however, general rule applies to any Arduino.
First, you need to check Arduino pin mapping:
Then, let's assume you want to use digital pin 2, so PD2 on Atmega168/328. (PD2 is short for PORTD pin 2). To use it as an input you need to do:
DDRD &= ~(1 << PD2);
DDRD is data direction register for port D. Whole operation sets bit corresponding to pin 2 to 0.
Then to read this pin:
if (PIND & (1<<PD2)) {
// do something
}
Also, please check, how to manipulate single bits: How do you set, clear, and toggle a single bit?

Interrupt with KL25Z board using KDS

I am programming a microcontroller using Kinetis Design Studio(KDS) and i want to implement an interrupt such that at anytime during runtime if a specific I/O pin has a "high" level the microcontroller should stop working until the pin is back to "low" .
Any ideas?
Try this code from free scale forum
https://www.element14.com/community/community/designcenter/kinetis_kl2_freedom_board/blog/2015/08/21/kinetis-design-studio-30-kinetis-sdk-12-processor-expert-using-frdm-kl43z-quick-tutorial-on-gpio-interrupt-toggling-led-using-button-with-interrupt-logic
or
below is an usart interrupt code you can modify it
/* p6_4.c UART0 Receive using interrupt */
/* This program modifies p4_2.c to use interrupt to handle the UART0 receive.
Receiving any key from terminal emulator (TeraTerm) of the host PC to the UART0 on Freescale FRDM-KL25Z board.
UART0 is connected to openSDA debug agent and has a virtual connection to the host PC COM port.
Launch TeraTerm on a PC and hit any key.
The LED program from P2_7 of Chapter 2 is used to turn on the tri-color LEDs according to the key received.
By default in SystemInit(), FLL clock output is 41.94 MHz.
Setting BDH=0, BDL=0x17, and OSR=0x0F yields 115200 Baud. */
#include <MKL25Z4.H>
void UART0_init(void);
void delayMs(int n);
void LED_init(void);
void LED_set(char value);
int i=0;
int main (void) {
__disable_irq(); /* global disable IRQs */
UART0_init();
LED_init();
__enable_irq(); /* global enable IRQs */
while (1) {
/* UART0 receive is moved to interrupt handler*/
}
}
/* UART0 interrupt handler */
void UART0_IRQHandler(void)
{
char c;
c = UART0->D; /* read the char received */
LED_set(c); /* and use it to set LEDs */
}
/* initialize UART0 to receive at 115200 Baud */
void UART0_init(void)
{
SIM->SCGC4 |= 0x0400; /* enable clock for UART0 */
SIM->SOPT2 |= 0x04000000; /* use FLL output for UART Baud rate generator */
UART0->C2 = 0; /* turn off UART0 while changing configurations */
UART0->BDH = 0x00;
UART0->BDL = 0x17; /* 115200 Baud */
UART0->C4 = 0x0F; /* Over Sampling Ratio 16 */
UART0->C1 = 0x00; /* 8-bit data */
UART0->C2 = 0x24; /* enable receive and receive interrupt*/
NVIC->ISER[0] |= 0x00001000; /* enable INT12 (bit 12 of ISER[0]) */
SIM->SCGC5 |= 0x0200; /* enable clock for PORTA */
PORTA->PCR[1] = 0x0200; /* make PTA1 UART0_Rx pin */
}
/* initialize all three LEDs on the FRDM board */
void LED_init(void)
{
SIM->SCGC5 |= 0x400; /* enable clock to Port B */
SIM->SCGC5 |= 0x1000; /* enable clock to Port D */
PORTB->PCR[18] = 0x100; /* make PTB18 pin as GPIO */
PTB->PDDR |= 0x40000; /* make PTB18 as output pin */
PTB->PSOR |= 0x40000; /* turn off red LED */
PORTB->PCR[19] = 0x100; /* make PTB19 pin as GPIO */
PTB->PDDR |= 0x80000; /* make PTB19 as output pin */
PTB->PSOR |= 0x80000; /* turn off green LED */
PORTD->PCR[1] = 0x100; /* make PTD1 pin as GPIO */
PTD->PDDR |= 0x02; /* make PTD1 as output pin */
PTD->PSOR |= 0x02; /* turn off blue LED */
}
/* turn on or off the LEDs according to bit 2-0 of the value */
void LED_set(char value)
{
if (value=='c')
{ /* use bit 1 of value to control green LED */
PTB->PCOR = 0x80000; /* turn on green LED */
s }
if (~(value=='c') )
{ /* use bit 1 of value to control green LED */
PTB->PSOR = 0x80000; /* turn off green LED */
}
}

GPIO mode register

I've adjusted the example from here for the STM3240G-EVAL board in order to blink LEDs 3 and 4. I have it working, but am confused by the Mode register setting:
GPIOG->MODER |= (GPIO_MODER_MODER6_0 | GPIO_MODER_MODER8_0) ;
When I read the reference manual (p186), it claims that the mode must be set to 01 for output, yet setting it to 0 in this way works just fine. Ideally I'd like to be able to change to the other modes, but I would have assumed that the above code would have changed pins 6 and 8 of port G to input pins. I must be missing something.
Here's my complete main document in case it's relevant:
#include "stm32f4xx.h"
/* We will use PG6 and PG8 connected to LEDs 1 and 2 because they're the same port. */
/* Find base register value for Port G */
void delay (int a);
int main(void)
{
/*!< At this stage the microcontroller clock setting is already configured,
this is done through SystemInit() function which is called from startup
file (startup_stm32f0xx.s) before to branch to application main.
To reconfigure the default setting of SystemInit() function, refer to
system_stm32f0xx.c file
*/
/* GPIOG Periph clock enable */
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;
GPIOG->MODER |= (GPIO_MODER_MODER6_0 | GPIO_MODER_MODER8_0) ;
/* Configure PG6 and PG8 in output mode */
GPIOG->OTYPER &= ~(GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_8) ;
// Ensure push pull mode selected--default
GPIOG->OSPEEDR |= (GPIO_OSPEEDER_OSPEEDR6|GPIO_OSPEEDER_OSPEEDR8);
//Ensure maximum speed setting (even though it is unnecessary)
GPIOG->PUPDR &= ~(GPIO_PUPDR_PUPDR6|GPIO_PUPDR_PUPDR8);
//Ensure all pull up pull down resistors are disabled
while (1)
{
/* Set PG6 and PG8 */
/* the bit set/reset low register SETS the output data register */
/* the bit set/reset high register RESETS the output data register */
GPIOG -> BSRRL = (1 << 6);
GPIOG -> BSRRL = (1 << 8);
delay(500000);
/* Reset PC8 and PC9 */
GPIOG -> BSRRH = (1 << 6);
GPIOG -> BSRRH = (1 << 8);
delay(500000);
}
return 0;
}
void delay (int a)
{
volatile int i,j;
for (i=0 ; i < a ; i++)
{
j++;
}
return;
}
You aren't setting it to zero, you're setting it to one.
The definition of the GPIO_MODER_MODER6_0 constant is 0x00001000. The mask for the GPIO_MODER_MODER6 bits is 0x00003000, so you're putting bits 01 into the right place.
If the constant GPIO_MODER_MODER6_0 were defined as zero, then or'ing it into the configuration register would have no effect in any case. To set both bits to zero you'd need to do something like:
GPIOG->MODER &= ~(GPIO_MODER_MODER6_0 | GPIO_MODER_MODER6_1);
The _0 and _1 suffixes refer to the bit numbers for masking, not the values being written.

How to read temp from DS18B20 using UART board

I bought board like this now i want to connect temperature sensor to this board.
How to read temperature from sensor in c or c++?
I tried to write some code but it won't work. I connect DS18B20 data cabel directly to TXD and RXD pins.
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <cstring>
#include <inttypes.h>
#include <errno.h>
int
set_interface_attribs (int fd, int speed, int parity)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tcgetattr";
return -1;
}
cfsetospeed (&tty, speed);
cfsetispeed (&tty, speed);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~IGNBRK; // ignore break signal
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read doesn't block
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl
tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag |= parity;
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr (fd, TCSANOW, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tcsetattr";
return -1;
}
return 0;
}
void
set_blocking (int fd, int should_block)
{
struct termios tty;
memset (&tty, 0, sizeof tty);
if (tcgetattr (fd, &tty) != 0)
{
std::cout<<"error "<<errno<<" from tggetattr";
return;
}
tty.c_cc[VMIN] = should_block ? 1 : 0;
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
if (tcsetattr (fd, TCSANOW, &tty) != 0)
std::cout<<"error "<<errno<<" setting term attributes";
}
int main()
{
char *portname = "/dev/ttyUSB0";
int tty_fd = open (portname, O_RDWR | O_NOCTTY | O_SYNC);
if (tty_fd < 0)
{
std::cout<<"error "<<errno<<" opening "<<portname<<": "<< strerror (errno);
return -1;
}
set_interface_attribs (tty_fd, B9600, 0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking (tty_fd, true);
unsigned char c = 0xCC;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error";
sleep(2);
unsigned char buffer[8];
int size;
if((size = read(tty_fd, &buffer, 8)) < 0)
std::cout<<"Error";
else
std::cout<<"CC("<<size<<")='"<<buffer<<"'";
std::cout<<"\n";
c = 0x44;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error2";
c = 0xBE;
if(!write(tty_fd, &c, sizeof(c)))
std::cout<<"Write error2";
sleep(2);
if((size = read(tty_fd, &buffer, 8)) < 0)
std::cout<<"Error";
else
std::cout<<"BE("<<size<<")='"<<buffer<<"'";
std::cout<<"\n######################\n";
close(tty_fd);
}
I got:
CC(1)='Č#'
BE(2)='#ž#'
######################
CC(1)='Č#'
BE(2)='#ž#'
######################
CC(1)='Č#'
BE(2)='#ž#'
######################
Can you help me?
You can't do this with any software. The DS18B20 is electrically incompatible with the board you have. The sensor uses a 1-wire, open-collector communication scheme that is completely different from the serial protocol normally used with this board. With great difficulty you might be able to do some bit-banging of the RTS/CTS signals but you need circuitry to combine them into a bidirectional open-collector signal.
You can hack the UART to communicate with 1 wire protocol.
Connect Rx to Tx and add 4.7 pull-up resistor
See the application note from maxim:
http://www.maximintegrated.com/en/app-notes/index.mvp/id/214
As pointed out by user3804701, it's indeed possible to communicate with a 1-Wire device using a UART interface, and the application note at https://www.maximintegrated.com/en/app-notes/index.mvp/id/214 contains all the information needed to make it work.
But the OP's code needs several fixes:
Each transaction with DS18B20 is made of 3 steps: initialization (also called reset), ROM command and function command, optionally followed by a data exchange
The initialization or reset step is performed by configuring the UART with 9600 bps baud rate, transmitting 0x0F, and receiving a dummy byte; then, the baud rate must be set to 115200 bps to perform the next steps
Following the reset step, data is sent to DS18B20 by writing to the UART a 0xFF byte for each data bit set to 1, and a 0x00 byte for each bit set to 0, starting from the least significant bit; for example, to send 0xAB (i.e. 10101011), one would write to the UART the sequence (FF FF 00 FF 00 FF 00 FF); for each byte written to the UART, there is a "return byte" that needs to be read from the UART and discarded
Data is received from DS18B20 by sending the 0xFF byte following the rules in the previous bullet point, but instead of discarding the "return bytes" reading from the UART a byte for each data bit, starting from the least significant bit: a 0xFF value means that the bit value is 1, otherwise the bit value is 0; for example, the sequence (00 FF FF 00 FF 00 00 FF) read from the UART means that DS18B20 sent 0x96 (i.e. 10010110)
If there is just one DS18B20 connected to the 1-Wire bus, all transactions can use "skip ROM" (byte value 0xCC) as ROM command
The first transaction to be performed is the one that triggers a temperature conversion from DS18B20, with function command 0x44
After waiting for the conversion to complete (which can take up to 750 ms), the host can perform a second transaction to read the DS18B20 scratchpad memory (function command 0xBE); the scratchpad memory is 9 bytes long and contains among other things the temperature value
So in summary the steps needed to get a temperature sample from DS18B20 are: reset, write 0xCC, write 0x44, wait for conversion, reset, write 0xCC, write 0xBE, read 9 bytes.
Example code that implements this stuff is available at https://github.com/dword1511/onewire-over-uart.

Initialize the AD controller in IRQ mode

I'm trying to understand initializing an ADC on the ARM Cortex M4 MK20DX256VLH7 on the Teensy 3.1. I'm curious about the terminology and relevant search terms as to what the symbols below mean. &= ~(3<<18), which I interpret as a bitwise AND on a bitwise NOT(3 bitwise left shift 18), means very little to me. I interpret what is inside the parenthetical as BIN 11 shifts to BIN 11000000000000000000. I understand that there is a pointer dereference happening to PINMODE1 (which is a little fuzzy to me) and that it is initializing pin 25 on the chip as an ADC input? I am not at all confident in my ability to parse this. Please advise. Thank you for your time.
void ADC_Init (void) {
LPC_PINCON->PINMODE1 &= ~(3<<18); /* P0.25 */
LPC_PINCON->PINMODE1 |= (1<<18); /* has neither pull-up nor pull-down */
LPC_PINCON->PINSEL1 &= ~(3<<18); /* P0.25 is GPIO */
LPC_PINCON->PINSEL1 |= (1<<18); /* P0.25 is AD0.2 */
LPC_SC->PCONP |= (1<<12); /* Enable power to ADC block */
LPC_ADC->ADCR = (1<< 2) | /* select AD0.2 pin */
(4<< 8) | /* ADC clock is 25MHz/5 */
(1<<16) | /* Burst mode */
(1<<21); /* enable ADC */
// LPC_ADC->ADINTEN = (1<< 8); /* global enable interrupt */
// NVIC_EnableIRQ(ADC_IRQn); /* enable ADC Interrupt */
}