![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
reelthymes
Joined: 26 Jul 2024 Posts: 10
|
General advice - first time programmer |
Posted: Fri Jul 26, 2024 12:58 pm |
|
|
Hi,
I have tested embedded systems before but this is my first time developing firmware itself. I have a few questions. I have a device that already has an existing firmware codebase, I am just trying to take that code and make some modifications in order to add an additional functionality. This device is very simple, it is basically a 1P2T switch and it can be controlled by USB, as well as daisy chained as a slave device on both SPI and I2C interfaces. Here is some additional info of my device and environment:
MPLAB v8.92
PCD compiler
MCU is PIC24FJ256GB206
This is what I'm trying to do:
In the legacy code, the I2C interface of the device was used so that the device can be a slave device. For this revision, I will get rid of the I2C bus ( I believe it's defined in the SW using functions) and instead I will use the 3 lines that used to be used for the I2C address bits as lines that are inputs from an external push button. So basically the switch can be controlled via an external push button.
I noticed that the original code had a few lines that enabled interrupts:
Code: | enable_interrupts(INTR_ALTERNATE); // BootLoader use the Default INTR
enable_interrupts(INTR_GLOBAL); // enable interrupts in general to be active
enable_interrupts(INT_SI2C2); |
does the global setting mean that any of the I/O pins can now serve as a source of interrupt?
In a different part of the code, those 3 lines that were designated as the I2C address lines were set to inputs using the function:
set_tris().
I will keep that call the same because I will still be using them as inputs from the push button.
I also saw several calls to set_pullup(TRUE, myPin);
But wouldn't we want all pins to be set to TRUE, because otherwise they wouldn't be pulled up and would be floating?
I know these may be very basic questions, so forgive me for that, I am just learning. Any inputs would be appreciated.
Thanks[/code] |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Fri Jul 26, 2024 1:52 pm |
|
|
You really need to read the chip data sheet.
_each interrupt has two enable bits that control whether it is accepted+.
It's own interrupt enable, and a global enable.
Both have to ve set before an interrupt will be handled.
The 'point' of the global bit is if you want to disable all the interrupts, this
can be disabled, rather than having to turn them all off individually.
On the pull-ups, this depends on the hardware. You need to look at
how the lines are wired. and how they are driven, So if the lines are
driven low, or have external connections they won't want pullups.
it actually uses fractionally less power to drive unused lines low, than
to enable the pullups. |
|
![](templates/subSilver/images/spacer.gif) |
reelthymes
Joined: 26 Jul 2024 Posts: 10
|
|
Posted: Fri Jul 26, 2024 1:57 pm |
|
|
Thanks for the info. I have been reading the Microchip's datasheet for the PIC24FJ256GB210 Family of devices. I am still learning what some of the lingo means, I also have the book "making embedded systems" which is helping. I will keep reading through the datasheet. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Sat Jul 27, 2024 9:24 am |
|
|
Good.
Key is a bit of history. On the older PIC's (12/14/16/18), there is an actual
global interrupt enable bit. On the 16bit PIC's, this is simulated by changing
the priority control. So setting the top three bits of the SRL, disables all the
normal interrupts (but not traps). While clearing these bits enables all
the interrupts/ The compiler retains the 'old' instruction, and using this
just turns the priority up and down (enable/disable). |
|
![](templates/subSilver/images/spacer.gif) |
reelthymes
Joined: 26 Jul 2024 Posts: 10
|
|
Posted: Wed Jul 31, 2024 7:47 am |
|
|
As a follow up to the interrupts discussion, I have a few more questions. I understand now that the interrupts are enabled via two bits (the global and individual bit).
When I read the data sheet for the PIC24 family, there is a lot of information relevant to interrupts and it also talks about all of the peripherals that exist on the chip (including I2C). In the case of my device it looks like there are two I2C buses used. One is used to communicate with an EEPROM chip and the other is used to interface with another identical device. Is there a way I can find out which one is software implemented and which one is using the "in-built" I2C bus of the chip? Or is my assumption about that incorrect? The datasheet says that I2C along with various other peripherals exist on this MCU, so to me that means that they are hardware implemented and that certain pins will be reserved for those peripherals.
however, my goal is to get rid of one of those I2C buses (the one used to daisy chain with another external device) and instead use the three formerly I2C address lines as inputs from my external push button.
What I did is used the call set_tris_d(SOME_MASK) to make those lines inputs. Since those lines are on port D, I am assuming that port D consists of general I/O lines.
Inside the main loop in my program, I am polling that port at each iteration with a call to
Code: | myDataWord = input_d(); |
I can then inspect the bits associated with that external push button and act accordingly. However, I believe that this is not really the correct approach as I really want to be writing an 'Interrupt Service Routine" (ISR) to handle the push button event?
If so, how to I go about setting those particular I/O lines on port D as "interrupt" lines? And what functions do I use to define an ISR for that event. I am assuming there are some CCS functions for that.
When I read the PIC24 manual I get even more overwhelmed because in the section discussing interrupts, it talks about an "interrupt controller" as well as "interrupt vector table' etc...
Do you have any advice for some basic first steps to understanding and implementing ISRs?
FOLLOW UP:
I do see that the following lines exists in the code:
Code: |
enable_interrupts(INT_SI2C2); |
and the #INT_SI2C2 directive is used before the definition of a function. According to the CCS compiler manual, this means "call this function when the SI2C2 interrupt is triggered".
So I guess I can use this same logic when developing my custom ISR. But the question is, how to do this for a custom ISR? I looked at some other examples on this forum and it looks like I might be able to use one of the external interrupts and define what it is. so now I am doing the following:
Code: | ////////////// Push-button Interrupt Service Routines ///////
#INT_EXT0
void nopen_interrupt()
{
output_b(5);
}
#INT_EXT1
void noclosed_interrupt()
{
output_b(11);
} |
and in my main routine I do the following:
Code: | enable_interrupts(INTR_GLOBAL); // enable interrupts in general to be active
enable_interrupts(INT_SI2C2);
enable_interrups(INT_EXT0);
enable_interrups(INT_EXT1); |
But the question I have is, how can I associate the INT_EXT0 and INT_EXT1 with certain trigger events? In other words, I know what my ISRs are, but how to define what triggers them? In my case they are a couple of input lines on port D. If they go HIGH, i want the interrupt to be triggered. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Thu Aug 01, 2024 2:51 am |
|
|
Start in a number of places:
First look at the examples with the compiler. Several of your questions have
answers in these. These are vital learning stuff.
Then look at the file 'ints.txt' with the compiler. This tells you the compller
interrupt names and what they are. Now this will immediately tell you that
one of the interrupts you are enabling may not be what you think it is.
INT_SI2C2 is the I2C 2 _slave_ interrupt. Now seriously an I2C slave is
a complex piece of code. You don't want to be touching things like this
until you are very happy with basic operations. Walk before riding applies
here. An I2C slave cannot be done in software. Too slow reacting to the
events on the bus.
It was this interrupt that was handling the I2C slave device.
No the GLOBAL setting means the priority is setup to allow all enabled
interrupts to operate.
Now on the I2C, your code says which I2C peripherals to use.
You can setup I2C using software I2C (any pins you want, and the code
is generated to implement an I2C bus in software), Hardware using the
I2C1 peripheral or I2C2. For some of the peripherals on your chip there
is PIN_SELECT remapping available. I don't think your I2C peripherals
support this (again you need to read the data sheet). For peripherals
that support PPS, you have to specify what pins they use using
#PIN_SELECT (look at the sticky at the top of the forum about this).
In either case once this is set, or if the pins are not relocatable, you
can tell #USE_I2C to talk to I2C1, and this automatically sets the I2C
stream you use with this setup to talk to the I2C1 peripheral, not use
software. An I2C slave _must be hardware_.
Now your chip doesn't support PPS for the I2C, but does for the SPI,
timers, and UARTs.
Now on input notifications. There are a perhaps surprising number of
ways these can be done.
First, there are dedicated interrupt pins:
INT0, INT1 & INT2. These are dedicated pins and the interrupt will
only be triggered by these pins.
Then there is input capture. This uses a dedicated timer, and counts
changes on a pin.
Then on a lot of the older chips there are dedicated 'change' pins that
trigger an interrupt when they change from the last read state. Normally
Port B. However your chip has the last option.
Input change. Here you have to set which pins notification is to be triggered
by and this triggers the CNIF bit. Some chips have multiple interrupts
for this, your I think, only has one.
There is also a finial slightly unexpected method of detecting a change
by using the comparator peripheral.
So let's assume you intend to use input change. This can only be used on
pins that have 'CNxx' in the data sheet. Most pins on your chip.
Look in the examples at EX_CNI.C this shows how you you setup this
interrupt on (in the example) up to three pins, and then how the
interrupt is polled internally to see which of the pins has triggered.
The interrupt controller is the hardware in the chip that performs a huge
number of things. First it sets the interrupt flag when an event which
triggers an interrupt happens. Then if the flag has been set to say
that an interrupt of this priority level should be handled (GLOBAL), and
the flag enabling this interrupt is also set,
when the processor is in the part of an instruction where an interrupt
can be handled, it raises the processor's priority setting to that of the
interrupt (thereby blocking all lower and equal priority interrupts), and
loads the vector for this interrupt from the IVT. It then pushes the
current code location onto the stack, and jumps to the vector for the
interrupt.
Both of your EPROM's may well be using a single I2C bus. I2C is a
multi-slave bus. Each device on the bus has a software address and
on EEPROM's this is normally set from perhaps eight possibilities by
setting particular pins high/low. So I'd suspect the addresses of the
two EEPROM's have been set to different values, and they are on a
single bus. |
|
![](templates/subSilver/images/spacer.gif) |
reelthymes
Joined: 26 Jul 2024 Posts: 10
|
|
Posted: Thu Aug 01, 2024 10:12 am |
|
|
Thank you for all the valuable info. I think I have overcomplicated my initial description. After further reading into the compiler and chip, I think I found an approach that will work. My use case is very simple, I simply want to use two I/O pins as interrupts for my device to perform an action (forget about the I2C bus for now). Here is how I am now doing it. First I define the following outside of my main() function:
Code: | // Set the D1 and D2 pins as interrupts
#pin_select INT1=PIN_D1
#pin_select INT2=PIN_D2 |
Next, I define those interrupt service routines:
Code: | ////////////// Push-button Interrupt Service Routines ///////
#INT_EXT1
void ext1_interrupt_service()
{
output_b(SOME_MASK);
}
#INT_EXT2
void ext2_interrupt_service()
{
output_b(SOME_OTHER_MASK);
} |
Finally, at the top of my main() function, I do the following:
Code: | enable_interrupts(INT_EXT1);
enable_interrupts(INT_EXT2); |
Code: | ext_int_edge(1,L_TO_H); // interrupt will occur when the signal goes from low to high
ext_int_edge(2,L_TO_H); |
Does this generally look like I'm on the right path? As for the I2C bus, the relavance to this situation is that pins D0, D1, D2 used to be the pins used for the I2C slave address. In my update, I am no longer utilizing an I2C bus for this device, so I am simple using pins D1 and D2 as interrupt triggers. Instead of those lines being used for I2C address bits, they will be wired to a physical push button that will trigger the interrupt services. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Thu Aug 01, 2024 12:23 pm |
|
|
One huge problem.
The interrupts are _NOT_ PPS devices. They are on fixed pins.
As I said:
"These are dedicated pins".
There is a simple solution, forget interrupts on the pins. Have a timer
interrupt at (say) 10mSec, and accept the signal when it is high for
two successive interrupts. This then gives debouce, which is vital
when handling keys. |
|
![](templates/subSilver/images/spacer.gif) |
reelthymes
Joined: 26 Jul 2024 Posts: 10
|
|
Posted: Fri Aug 02, 2024 6:51 am |
|
|
Thanks for sharing that vital info, I missed that. I see now that I can't use those pins as interrupts as the interrupts are not PPS, but are dedicated pins.
So if I were to use the suggested approach of a timer interrupt, could I still use those pins I mentioned (D1 and D2), because both of those have 'CN' according to the data sheet?
I would be looking at the input change on those pins, and using an timer interrupt to poll them? From the compiler manual, it looks like the timer interrupt will fire when it overflows, at which point I can check the state of the pins. (and do that at two or more successive interrupts). |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9168 Location: Greensville,Ontario
|
|
Posted: Fri Aug 02, 2024 7:15 am |
|
|
Search the library or here for 'BUTTON' ! years ago someone, somewhere cut CCS code to work like the PBASIC ( aka 'stamp' ) BUTTON function.
My memory is fuzzy, but I recall it did work GREAT,thought it'd be a nice function to add to the compiler.
this....
http://www.ccsinfo.com/forum/viewtopic.php?t=23837&highlight=button
may be it..... |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Fri Aug 02, 2024 8:12 am |
|
|
Yes.
For CN, as I said: "Look in the examples at EX_CNI.C". However there is
the huge issue of bounce when using mechanical switches with an
interrupt.
You could all capacitive filtering to the buttons (resistor and tiny capacitor),
to get rid of debounce. |
|
![](templates/subSilver/images/spacer.gif) |
reelthymes
Joined: 26 Jul 2024 Posts: 10
|
|
Posted: Mon Aug 05, 2024 9:34 am |
|
|
Going back to an earlier point, are you sure that PPS can't be used with external interrupts? According to the data sheet for the PIC24FJ256GB210 family of devices Quote: | The peripherals managed by the PPS are all digital
only peripherals. These include general serial communications (UART and SPI), general purpose timer clock
inputs, timer related peripherals (input capture and output compare) and external interrupt inputs. |
There is also a table that lists remappable functions. INT1-INT4 are included in this table. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Mon Aug 05, 2024 12:26 pm |
|
|
You are right on this chip. On most chips they are not relocatable.
However using the interrupts, you will have problems with bounce.
Understand they a simple keyboard may well give a dozen or more
makes when a key is pushd. It depends massively. on the quality of
the contacts, and how the user pushes the key |
|
![](templates/subSilver/images/spacer.gif) |
jeremiah
Joined: 20 Jul 2010 Posts: 1328
|
|
Posted: Mon Aug 05, 2024 12:44 pm |
|
|
Ttelmah wrote: | On most chips they are not relocatable. |
Just adding clarification. On most PIC24FJ family chips, the general case is actually the opposite of this statement. With this family, interrupts INT1 and higher are generally relocatable on most chips. Sometimes INT1 is not on chips where you need it for deep sleep interrupts or similar.
This may be different from PIC18s and lower though. I don't recall if dsPICs or 16bit chips outside of PIC24FJ are different off the top of my head. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19358
|
|
Posted: Wed Aug 07, 2024 11:37 am |
|
|
Yes, again it is specific to each different sub family.
However I was trying to 'discourage' the use of interrupts, since it is a very
common thing that causes no end of problems. Without external filtering,
or using expensive keyboard designs with things like hall switches, or
designed specifically made to not generate bounce, using interrupts for
a keyboard requires a lot more code to add debounce handling etc., and
is generally a poor way to actually do this job.... ![Sad](images/smiles/icon_sad.gif) |
|
![](templates/subSilver/images/spacer.gif) |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|