|
|
View previous topic :: View next topic |
Author |
Message |
savino
Joined: 07 Apr 2014 Posts: 1
|
How to use #pin_select handling ANSEL, and slew rate control |
Posted: Thu Apr 21, 2016 5:30 pm |
|
|
In my project in setting I2c through CCS WIZARD I have this error when compiling.....
Option invalid. Wrong pin for H/W.
This my code:
file I2C.h
Code: |
#include <16LF1704.h>
#device ADC=10
#use delay(internal=32MHz)
#use STANDARD_IO( C )
#define Device_SDA PIN_C1
#define Device_SLC PIN_C0
#use i2c(Slave,Slow,sda=Device_SDA,scl=Device_SLC,address=0x10)
|
file I2C.c
Code: |
#include <I2C.h>
void main()
{
while(TRUE)
{
//TODO: User Code
}
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Thu Apr 21, 2016 8:47 pm |
|
|
Put this above the #use i2c() statement:
Code: | #pin_select SCL1OUT = PIN_C0
#pin_select SCL1IN = PIN_C0
#pin_select SDA1OUT = PIN_C1
#pin_select SDA1IN = PIN_C1 |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Fri Apr 22, 2016 4:33 am |
|
|
This is one that is approaching 'FAQ' status. Perhaps ought to be a sticky at the top of the forum. It is now!...
PIC's have three different ways of selecting pins that peripherals go to:
The first (oldest), is in some cases individual peripherals can be set to two different sets of pins using a fuse. So you have chips with fuses like 'ALTI2C1', that sets the I2C1 peripheral to use the alternate pins.
Then there were chips with the 'first generation' re-mappable peripherals, where there is a single configuration bit that moves a peripheral to another set of pins (exactly like the fuse, but accessible in software).
This is APFCON. If your chip has this register then you simply need to
set the bit in this register.
Then there is the latest version 'Peripheral Pin Select' (PPS).
It is 'PPS' chips that #PIN_SELECT is for.
On these there are a number of pins, that support 're-mappable peripherals' (normally shows as 'RP...' in the pin diagrams, and a number of peripherals that can be moved to these. On these, you must tell the compiler what pins the peripheral is using before setting the peripheral up.
Just to expand on PCM_programmer's answer, I'd also suggest using the peripheral name, rather than the pins in the #use statement. So:
Code: |
#include <16LF1704.h>
#device ADC=10
#use delay(internal=32MHz)
#use STANDARD_IO( C )
#pin_select SCL1OUT = PIN_C0
#pin_select SCL1IN = PIN_C0
#pin_select SDA1OUT = PIN_C1
#pin_select SDA1IN = PIN_C1
#use i2c(Slave,I2C1,address=0x10)
|
So this sets the peripheral I2C1 SCL I/O functions to go to PIN_C0 and the SDA to go to PIN_C1.
Then the #use I2C, can talk to the hardware peripheral (I2C1), and it's external connections will be to these pins.
As a comment to the poster at the start of this thread, get rid of 'slow'. A _slave_ does not have a speed in general. The speed is controlled and settable in the master only. Can cause issues in the configuration.
Now to expand for other devices. What I/O devices can use this?.
This depends on your chip. Data sheet, and look at the header file for the chip. So (for instance), the chip header may well say something like:
Code: |
////////////////////////////////////////////////////////////////// PIN_SELECT
// #pin_select function=pin
// Valid Pins:
// PIN_B0,PIN_B1,PIN_B2,PIN_B3,PIN_B4,PIN_B5,PIN_B6,PIN_B7,PIN_B8,PIN_B9,
// PIN_B10,PIN_B11,PIN_B12,PIN_B13,PIN_B14,PIN_B15,PIN_A0,PIN_A1
// Input Functions:
// OCTRIG1,INT1,INT2,INT3,INT4,OCTRIG2,T2CK,T3CK,CCP1,CCP2,CCP3,CCP4,IC1,IC2,
// IC3,OCFA,OCFB,CCPCKIA,CCPCKIB,U1RX,U1CTS,U2RX,U2CTS,SDI1,SCK1IN,SS1IN,SDI2,
// SCK2IN,SS2IN,TMRCLK,CLCINA,CLCINB,SDI3,SCK3IN,SS3IN
// Output Functions:
// NULL,C1OUT,C2OUT,U1TX,U1RTS,U2TX,U2RTS,SDO1,SCK1OUT,SS1OUT,SDO2,SCK2OUT,
// SS2OUT,OC1,OC2,OC3,CCP2OUTA,CCP2OUTB,CCP3OUTA,CCP3OUTB,CCP4OUTA,CCP4OUTB,
// SDO3,SCK3OUT,SS3OUT,C3OUT,PWRGT,REFCLK,CLC1OUT,CLC2OUT,RTCCOUT
//
|
So this chip has a huge array of peripherals that can be used this way (four interrupt pins, four CCP's, Uart1, Uart2, SSP2 etc., and a lot of pins that can be used.
To setup the UART for pins B11 (RX) and B10 (TX), the syntax is:
Code: |
#PIN_SELECT U1RX=PIN_B11
#PIN_SELECT U1TX=PIN_B10
#USE RS232 (UART1, BAUD=115200, ERRORS)
//You should always use 'ERRORS' unless you are adding code to
//handle hardware ERRORS yourself
|
In addition to this, it is possible if required to 'dynamically' change the mapping later 'in code'. This is only needed if you have some specific reason to 'change things'. So (perhaps) you want to do a serial receive on another set of pins for a little while, and want the hardware UART on these.
Things needed:
1) Map the peripherals to the 'default' setting in the setup as shown.
2) Have the fuse NOIOL1WAY. This allows the settings to be used more than once after the chip boots.
3) Then you can change mappings 'in code', so (for instance) to move the above UART to pins B12, and B13:
Code: |
//Inside your code when wanted
putc('X'); //this will be using the default pins
delay_ms(50); //ensure byte has finished sending.
pin_select("U1RX", PIN_B12, TRUE, FALSE);
pin_select("U1TX", PIN_B13, FALSE, TRUE);
putc('Y');
//Now sent using B13
|
The 'TRUE' and 'FALSE' stuff is to do with unlocking the pin. Basically if you are doing a number of changes, then unlock on the first command, and lock up again on the last one. The 'TRUE' on the first command unlocks, then the 'FALSE says "don't lock back up", while on the next command the pins are already unlocked (so 'FALSE' for the unlock), but then lock back up when finished (final 'TRUE'). Keeping the pins locked when not being changed, avoids accidental unwanted changes.....
The default if these are omitted is to unlock and re-lock every time, but this takes longer.
There are sometimes some oddities that need to be carefully watched for.
An example here is the clock shown in the I2C setup, where there is both an 'in' and 'out' separated internally, and both have to be mapped to the single SCL pin if the peripheral is to work as expected. This is mentioned in the data sheet, so 'beware' and make sure you know what has to be mapped where....
One 'caveat' to add to this, applies with bootloaders.
If you have a bootloader, and it uses a PPS peripheral (so maps anything
with PPS), you need to ensure the NOIOL1WAY fuse is selected. Otherwise
your main code will not be able to change the pin settings.
The IOL1WAY fuse means that things can be set only once. Problem is if
the bootloader is setting them, then the 'main' code can't set them again....
Just adding a note about 'limitations' on this.
On the first chips with this (PIC24's), the capabilities were remarkably
'universal'. The chips had many pins all of which can be used by any
peripheral. However on the PIC18's with this ability the mappings
available are more restricted. Usually there are perhaps 32 destinations
for a particular pin. So on some of the chips with perhaps 64 pins,
the peripherals can only be selected to pins on some specific ports.
So though a great ability, a careful read of the data sheet is needed,
before 'assuming' a peripheral can go to a particular pin.
Read carefully....
Last edited by Ttelmah on Mon Nov 07, 2022 3:56 am; edited 2 times in total |
|
|
kda406
Joined: 17 Sep 2003 Posts: 97 Location: Atlanta, GA, USA
|
|
Posted: Fri Oct 18, 2019 8:04 am |
|
|
The above shows you how to setup mappable PPS chips for I2C and RS232. Here are some others for this sticky post.
PWM
One might expect to use #pin_select CP4OUT=PIN_G3 for example, but I have not been able to get this to work on V5 compilers. Here is an example of how I set two PWM outputs on pins G3 and G4:
Code: | #use pwm(CCP4, output=PIN_G3, TIMER=2, FREQUENCY=80, DUTY=50, STREAM=PWM_FAN) // Fan ECM
#use pwm(CCP5, output=PIN_G4, TIMER=6, FREQUENCY=250, DUTY=50, STREAM=PWM_PUMP) // Variable speed pump | PWM working values are set like this: Code: | pwm_set_duty_percent(PWM_FAN,mDC); // Set the fan's PWM value
pwm_set_duty_percent(PWM_PUMP,1000); // Set the pump's PWM value |
NOTE: the pwm_set_duty_percent() uses a 16 bit integer with a range of 0-1000 with an implied decimal for setting the value. To set the PWM to 85.7%, you pass 857.
DAC & SPI
This snippet from a header allows me to use the internal 5 bit DAC or an external SPI 8 bit DAC. A ProjectConfig.h file defines which DAC to use and this header and the code snippets below compile appropriately.
Code: | #if defined(USE_DAC_INTERNAL)
// DAC does NOT use PPS on this chip, but does have two channels
#define DAC_CHAN DAC_OUTPUT // Define which channel to use ("DAC_OUTPUT" channels are defined in the device header file)
#elif defined(USE_DAC_MCP48FVB02)
///// External SPI DAC /////
// PPS definitions //
#pin_select SCK2OUT = PIN_D6
#pin_select SCK2IN = PIN_D6
#pin_select SDO2 = PIN_D4
#pin_select SDI2 = PIN_D5
#use spi(SPI2,BITS=8,MODE=0, BAUD=1000000)
#define DAC_CS PIN_D1 // Chip select
#define DAC_LATCH PIN_D0 // The external DAC needs a latch signal
#endif |
GOTCHA: Yes, you must define SCK2OUT and SCK2IN as the same pin when you are an SPI Master!
DAC
With the above header file compiled for use with the internal DAC, setting a value is as simple as the following (value 0-31):
Code: | setup_dac(DAC_VSS_VDD | DAC_CHAN); // Setup DAC, referenced to chip power & ground. Vref+ cannot be used on this target.
dac_write(18); // Set the internal DAC to 18 |
SPI
SPI is setup similar to I2C in the header above. As an example, here is a read function for a MCP48FVB02 SPI device.
Code: | UINT16 MCP48FVB02_Generic_Read(UINT8 iWhere) {
UINT8 iLow,iHigh; // Some 8 bit holder variables to make things easy
iWhere <<= 3; // Shift the address up 3 bits
iWhere |= 0x07; // Put two 1s to send the read command, with a blank for the CMDERR bit
output_low(DAC_CS); // Select the SPI DAC
spi_write2(iWhere);
iHigh=spi_read2(0); //read a value from the SPI device
iLow=spi_read2(0); //read a value from the SPI device
output_high(DAC_CS); // Deselect the SPI DAC
return(make16(iHigh,iLow));
} |
For cross-platform compatibility, I have a header file that defines the following (and much more) for CCS use as: Code: | #define UINT8 unsigned int8
#define UINT16 unsigned int16 |
|
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Tue Jun 16, 2020 1:56 pm |
|
|
Hi, I'm a little confused regarding my specific pic and how to assign the pins.
Below is a list of the valid functions:
Code: | // Input Functions:
// INT0,INT1,INT2,T0CK,T1CK,T1G,T3CK,T3G,T5CK,T5G,T2CK,T4CK,T6CK,CCP1,CCP2,
// CCP3,PWM1RESET,PWM2RESET,PWM3RESET,PWMIN0,PWMIN1,SMT1WIN,SMT1SIG,CWG1IN,
// CWG2IN,CWG3IN,MDCARL,MDCARH,MDSRC,CLCIN0,CLCIN1,CLCIN2,CLCIN3,CLCIN4,
// CLCIN5,CLCIN6,CLCIN7,ADCACT,SCK1IN,SDI1,SS1IN,SCK2IN,SDI2,SS2IN,SDA1IN,
// SCL1IN,U1RX,U1CTS,U2RX,U2CTS,U3RX,U3CTS,U4RX,U4CTS,U5RX,U5CTS,T0CKI,T1CKI,
// T3CKI,T5CKI,T2CKI,T4CKI,T6CKI,CCP1IN,CCP2IN,CCP3IN,RX1,RX2,RX3,RX4,RX5
// Output Functions:
// NULL,CLC1OUT,CLC2OUT,CLC3OUT,CLC4OUT,CLC5OUT,CLC6OUT,CLC7OUT,CLC8OUT,
// CWG1OUTA,CWG1OUTB,CWG1OUTC,CWG1OUTD,CWG2OUTA,CWG2OUTB,CWG2OUTC,CWG2OUTD,
// CWG3OUTA,CWG3OUTB,CWG3OUTC,CWG3OUTD,CCP1OUT,CCP2OUT,CCP3OUT,PWM1S1P1,
// PWM1S1P2,PWM2S1P1,PWM2S1P2,PWM3S1P1,PWM3S1P2,U1TX,U1DE,U1RTS,U2TX,U2DE,
// U2RTS,U3TX,U3DE,U3RTS,U4TX,U4DE,U4RTS,U5TX,U5DE,U5RTS,C1OUT,C2OUT,SCK1OUT,
// SDO1,SS1OUT,SCK2OUT,SDO2,SS2OUT,SCL1OUT,SDA1OUT,T0OUT,NCO1OUT,NCO2OUT,
// NCO3OUT,CLKROUT,DSMOUT,ADGRDA,ADGRDB,TX1,DE1,TX2,DE2,TX3,DE3,TX4,DE4,TX5,
// DE5,SCK1,SCK2,SCL1,SDA1,TMR0OUT,NCO1,NCO2,NCO3 |
Specifically I'm trying to set all 5 uarts, but there are for each uart a U1TX and a TX1 - Which function is it!!!!
I'm reading the datasheet and its not working out for me...maybe i need more coffee.
PIC18F47Q43 _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Tue Jun 16, 2020 2:22 pm |
|
|
I've found by trial that its TX# & RX# the right function.
I still have no idea what U#TX and U#RX do...
I've defined my pins and declared my RS-232 functions but for some reason UART 5 throws an error:
Code: | #pin_select TX4 = U4_TX_PIN
#pin_select RX4 = U4_RX_PIN
#pin_select TX5 = U5_TX_PIN
#pin_select RX5 = U5_RX_PIN
#use rs232(UART1,baud=9600, xmit=U1_TX_PIN, rcv=U1_RX_PIN, ERRORS,STREAM=RS_485)
#use rs232(UART2,baud=9600, xmit=U2_TX_PIN, rcv=U2_RX_PIN, ERRORS,STREAM=GSM)
#use rs232(UART3,baud=9600, xmit=U3_TX_PIN, rcv=U3_RX_PIN, ERRORS,STREAM=MB2)
#use rs232(UART4,baud=9600, xmit=U4_TX_PIN, rcv=U4_RX_PIN, ERRORS,STREAM=FTDI)
//#use rs232(UART5,baud=9600, xmit=U5_TX_PIN, rcv=U5_RX_PIN, ERRORS,STREAM=ESP) |
C:\Users\Foo-Bar\Documents\1. PIC Developement\CCS Compiler\PIC18F47Q43\18F47Q43.X\Hardware.h:76:5: Error#100 USE parameter value is out of range UART5 defined, Use #PIN_SELECT to assign UART pins
I've clearly defined my pins, and selected them just like i did with all previous 4 uarts.... _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1354
|
|
Posted: Tue Jun 16, 2020 6:49 pm |
|
|
Gabriel wrote: | I've found by trial that its TX# & RX# the right function.
I still have no idea what U#TX and U#RX do...
I've defined my pins and declared my RS-232 functions but for some reason UART 5 throws an error:
Code: | #pin_select TX4 = U4_TX_PIN
#pin_select RX4 = U4_RX_PIN
#pin_select TX5 = U5_TX_PIN
#pin_select RX5 = U5_RX_PIN
#use rs232(UART1,baud=9600, xmit=U1_TX_PIN, rcv=U1_RX_PIN, ERRORS,STREAM=RS_485)
#use rs232(UART2,baud=9600, xmit=U2_TX_PIN, rcv=U2_RX_PIN, ERRORS,STREAM=GSM)
#use rs232(UART3,baud=9600, xmit=U3_TX_PIN, rcv=U3_RX_PIN, ERRORS,STREAM=MB2)
#use rs232(UART4,baud=9600, xmit=U4_TX_PIN, rcv=U4_RX_PIN, ERRORS,STREAM=FTDI)
//#use rs232(UART5,baud=9600, xmit=U5_TX_PIN, rcv=U5_RX_PIN, ERRORS,STREAM=ESP) |
C:\Users\Foo-Bar\Documents\1. PIC Developement\CCS Compiler\PIC18F47Q43\18F47Q43.X\Hardware.h:76:5: Error#100 USE parameter value is out of range UART5 defined, Use #PIN_SELECT to assign UART pins
I've clearly defined my pins, and selected them just like i did with all previous 4 uarts.... |
It might be chip specific. I use U1TX and U1RX and similar on all my PIC24s. I've never used just TX1 and RX1.
Also, avoid specifying the xmit and rcv pins in your #use rs232(). If you have done it correctly the pin_selects paired with the UART1, etc. will pick the correct pins and it avoid the issue where some versions of the compiler try to make software UARTS out of those directives instead of hardware. |
|
|
Gabriel
Joined: 03 Aug 2009 Posts: 1067 Location: Panama
|
|
Posted: Wed Jun 17, 2020 8:18 am |
|
|
Success. Thank you! compiled properly.
For 485, would this be a proper declaration then?
Code: | #use rs232(UART1,baud=9600, BITS=8, PARITY=N, STOP=1,ENABLE=U1_EN_PIN,ERRORS,STREAM=RS_485) //MODBUS |
I have defined U1_EN_PIN but I have not used #pin_select on this pin. The ISR was triggering and I was able to communicate with modbus properly. But now I'm wondering if the UART has a dedicated pin for this that I could/should define with pin select ? Uart 1 is the full featured uart on this chip. _________________ CCS PCM 5.078 & CCS PCH 5.093 |
|
|
jeremiah
Joined: 20 Jul 2010 Posts: 1354
|
|
Posted: Thu Jun 18, 2020 8:39 am |
|
|
I have not used RS485 on a CCS project, so I don't know for sure. It looks similar to what I have seen others use.
For a builtin solution, I would first reference the chip datasheet and see if there is a Peripheral Pin Select option for the 485 enable pin. If it is, the compiler probably supports it if I had to guess. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19538
|
|
Posted: Mon Jul 05, 2021 1:29 am |
|
|
I've decided to add a new post here about two other feature that are
appearing on a lot of the newer PIC's. ANSEL, and slew rate control.
Now on the older PIC's, we just had TRIS, and the analog multiplexers
for things like the ADC and comparator. A pin that was to be used as
'analog' was just set as an input with TRIS, and then connected to the
analog peripheral with (for the ADC), 'setup_adc_ports'. Nothing more
needed.
However a lot of the newer PIC's now have ANSEL registers. These
physically connect the 'analog' circuitry parts to the pin, but also
disconnect the digital input parts from the pin. Now on these PIC's
when they boot, the ANSEL registers have all the pins set as analog.
This will still allow you to use them as digital outputs, but can/will interfere
with the pins being used as digital inputs. The actual connection to
the digital gate, just is not connected!...
So there is a new setup line needed on such PIC's. set_analog_pins().
This on older PIC's does exactly the same as the setup_adc_ports
function. So you don't need to use it. However on the PIC's with ANSEL
registers, this is how you setup these registers.
So 'read the data sheet' Do a search for ANSEL. If it says that your chip
has such registers, you should modify your code.
At the start of your main, when you setup your ADC (if using this),
you need to add a set_analog_pins call, specifying the same pins that
you are using for the ADC. But more importantly perhaps, you need the
same call, even if you are not using the analog abilities at all. So:
Code: |
set_analog_pins(); //set none of the pins to analog.
|
Now as with most things here this depends on your compiler version.
On the latest compilers, this is the default for most chips. However even
on these, I have seen the occasional chip where this is not being set by
default. Also, again 'erratically', in some cases selecting an input with
setup_adc_ports, or connecting the comparator, does automatically
change the ANSEL, on quite a few it does not seem to do so.
So it is worth getting into the 'habit' of always explicitly setting this,
at the start, and whenever anything 'analog' is connected or disconnected.
Now the other similar new feature is slew rate control. Some of the
newest PIC's have this. The default value for this is that slew rate limiting
is enabled on all the pins. Now this is a great feature for reducing RF
noise, but of course when you do have a pin that needs to genuinely
change fast, will not allow this to happen. So things like:
High baud serial lines.
SPI
Fast PWM's and clocks
etc. etc..
All need the slew rate limiting turned off.
This is done with the set_slow_slew_x(val) statement. A '0' for a bit in this
disables slow slew, while a '1' enables it. So:
set_slow_slew_A(0b00000000);
turns off the slew rate limiting on PortA.
set_slow_slew_A(FALSE);
will do the same.
set_slow_slew_A(TRUE);
will turn it on for the whole port, while:
set_slow_slew_A(0b00000011);
will enable this on pins A0 & A1.
On all the chips with this feature, just like the ANSEL, it is another thing
that needs to be remembered. |
|
|
tinley
Joined: 09 May 2006 Posts: 67
|
|
Posted: Fri Aug 16, 2024 5:42 am |
|
|
I found an omission in the manual on this subject that could have saved me some time, so hopefully will save someone else the time if they read this.
I needed to have one software version that could detect the hardware and swap the SPI pins accordingly. It is important to deselect the unwanted SDOx pin first or else both the old and new pin will be connected to the peripheral.
pin_select ( "NULL", PIN_C3, 1, 0 ); // must deselect PIN_C3 first
pin_select ( "SDO1" , PIN_B3, 0, 0); // no unlock needed
pin_select ( "SDI1" , PIN_C3, 0, 1); // and lock again
Whilst the manual does mention that "NULL" can be used to deselect a pin, nowhere have I found it mention that you must do this if reassigning a pin.
This only applies to the output pins where the peripheral is assigned to the pin, whereas the input pins are assigned to the peripheral. |
|
|
|
|
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
|