External Links

Creative Science Centre

 

Inputs

This is not really an advanced topic but a repeat (more or less) of part 9 of the tutorial it may have some information for advanced users on how to set up registers for input and ADC. It is in fact a copy of the original tutorial.

Input has been left until now so that we can use the display as a neat device rather than using the terminal screen.

Digital Inputs

All of the ports can be configured to be either input or output and this is done with the TRIS register.

Input: Project 1

http://byvac.com/mBlib/flb/Tutorial/PIC32MX1_Family/30_Input/Project_1.bas

// Digital input
//
// Port A registers
constant ANSELA          0xBF886000
constant ANSELACLR       0xBF886004
constant ANSELASET       0xBF886008
constant ANSELAINV       0xBF88600C
constant TRISA           0xBF886010
constant TRISACLR        0xBF886014
constant TRISASET        0xBF886018
constant TRISAINV        0xBF88601C
constant PORTA           0xBF886020
constant LATA            0xBF886030
constant PORTACLR        0xBF886024
constant PORTASET        0xBF886028
constant PORTAINV        0xBF88602C
constant CNPUASET        0xBF886058

constant INP 1 << 0

// *****************************************************************************
// Initialise RA0 as in input, this is pin 1
// *****************************************************************************
function x()
dim count=0
	num_disp4(count)
	@TRISASET = INP // set as i/p
	@CNPUASET = INP // enable pull up
	@ANSELACLR =INP // set for digital I/O
	while  comkey?(2) = 0
		if (peek(PORTA) & INP) = 0 then
			count = count + 1
			num_disp4(count)
		endif
		if count > 9999 then;count=0;endif
	wend
endf

Connect a short length of wire to pin 2, this is RA0 and we are going to use this as an input. Load project_1 and have a look at function x() (end of the code)

INP is set to 1 at the top of the file. TRISASET sets the pin to be an input, CNPUASET sets a weak pull up resistor to keep the pin in a high state, there is also a weak pull down, finally ANSELACLR sets the pin to be a digital input rather than an analogue input.

Type seg_start and then x. The display should read 0. Now connect the other end of the short wire to ground (pin 8 is the nearest) and the counter will increment – a push button would do. No matter how quick you connect and disconnect the counter always counts several hundreds.

Suppose we wanted it to count just 1 each time it was connected to ground. One method is in project_2

Input: Project 2

http://byvac.com/mBlib/flb/Tutorial/PIC32MX1_Family/30_Input/Project_2.bas

// Digital input
//
// Port A registers
constant ANSELA          0xBF886000
constant ANSELACLR       0xBF886004
constant ANSELASET       0xBF886008
constant ANSELAINV       0xBF88600C
constant TRISA           0xBF886010
constant TRISACLR        0xBF886014
constant TRISASET        0xBF886018
constant TRISAINV        0xBF88601C
constant PORTA           0xBF886020
constant LATA            0xBF886030
constant PORTACLR        0xBF886024
constant PORTASET        0xBF886028
constant PORTAINV        0xBF88602C
constant CNPUASET        0xBF886058

constant INP 1 << 0

// *****************************************************************************
// Initialise RA0 as in input, this is pin 1
// *****************************************************************************
function x()
dim count=0
	num_disp4(count)
	@TRISASET = INP // set as i/p
	@CNPUASET = INP // enable pull up
	@ANSELACLR =INP // set for digital I/O
	while  comkey?(2) = 0
		if (peek(PORTA) & INP) = 0 then
			count = count + 1
			num_disp4(count)
			while (peek(PORTA) & INP) = 0
			wend
		endif
		if count > 9999 then;count=0;endif
	wend
endf

Load this project, type seg_start then x and see if it works.

The additional lines are the while and wend. What does happen is that when contact is made with ground the result is 0 and so the first ‘if’ lets the counter increment. The while statement does not let it go past until it is removed from the ground so why does it not work very well and sometimes count more than one?

This is called switch bounce, the fact is that no matter how good a switch is, and that includes the bit of wire when it is pressed on to the ground (pin 8 say) it bounces on the connector – touching – not touching etc. and because the processor reacts so fast, this is counted.

This is a practical matter as switches are an essential part of a control system and must be dealt with. The easiest, and probably best method is to simply introduce a delay.

Input: Project 2a

http://byvac.com/mBlib/flb/Tutorial/PIC32MX1_Family/30_Input/Project_2a.bas

// Digital input
//
// Port A registers
constant ANSELA          0xBF886000
constant ANSELACLR       0xBF886004
constant ANSELASET       0xBF886008
constant ANSELAINV       0xBF88600C
constant TRISA           0xBF886010
constant TRISACLR        0xBF886014
constant TRISASET        0xBF886018
constant TRISAINV        0xBF88601C
constant PORTA           0xBF886020
constant LATA            0xBF886030
constant PORTACLR        0xBF886024
constant PORTASET        0xBF886028
constant PORTAINV        0xBF88602C
constant CNPUASET        0xBF886058

constant INP 1 << 0

// *****************************************************************************
// Initialise RA0 as in input, this is pin 1
// *****************************************************************************
function x()
dim count=0
	num_disp4(count)
	@TRISASET = INP // set as i/p
	@CNPUASET = INP // enable pull up
	@ANSELACLR =INP // set for digital I/O
	while  comkey?(2) = 0
		if (peek(PORTA) & INP) = 0 then
			count = count + 1
			num_disp4(count)
			wait(100)
			while (peek(PORTA) & INP) = 0
			wend
			wait(100)
		endif
		if count > 9999 then;count=0;endif
	wend
endf

Load this project, type seg_start and then x. Not perfect but much improved. This just introduces one delay. Switch bounce lasts about 50mS or less, the bit of wire will need a longer time. As you can see delay is also required when going from 0 to 1.

To be the most accurate the function needs extending so that after the delay the port should be checked again, If it is still low then this is a valid on. Like wise when going from 0 (on) to 1 (off), after the delay the port can be checked to see if it is really 1.

Analogue

This processor is equipped with an analogue to digital converter so that an analogue signal such as a voltage can be read, digitised and then presented to a register as a number. For maximum flexibility there are numerous options that are all controlled by clearing or setting bits inside registers. All of which can be done of course using mB.

For this processor the following is available:

RA0   AN0
RA1   AN1
RB0   AN2
RB1   AN3
RB2   AN4
RB3   AN5
RB12  AN10
RB13  AN11
RB14  AN10
RB15  AN9

We will be using AN0 connected to RA0 (pin 2). First wire the trimmer as shown in the diagram. See the kit list item H

This is looking down on top of the trimmer, it should push easily into the breadboard. Make sure that you get the ground and +3V3 to the left and right of the trimmer (pins 1 and 2 on the trimmer). There will be smoke if you get one of these on the centre pin (pin 3). When the trimmer is turned there will be a voltage on the centre pin that will represent how much the trimmer has been turned. Fully to the ground side and there will be 0 volts, fully to the 3.3V side and there will be 3.3V. Anything in-between will be a portion of that. It is this voltage that we are going to measure on pin 2 (AN0)(pin 3 to the trimmer).

Input: Project 3

http://byvac.com/mBlib/flb/Tutorial/PIC32MX1_Family/30_Input/Project_3.bas

// Analogue input
//
// Registers
constant ANSELASET       0xBF886008
constant TRISASET        0xBF886018
constant AD1CON1         0xBF809000
constant AD1CON1SET      0xBF809008
constant AD1CON2         0xBF809010
constant AD1CON3         0xBF809020
constant AD1CSSL         0xBF809050
constant AD1CHS          0xBF809040
constant ADC1BUF0        0xBF809070
// adc
constant SAMPLE 5
constant CONVERSION 5
constant INP 1 << 0		// RA0

// *****************************************************************************
// Set up ADC for auto ample and scan
// *****************************************************************************
function adc_init()
	@TRISASET = INP // set as i/p
	@ANSELASET = INP // set as analogue
	@AD1CON3 = 0 // reset first, clock derived from PBCLK
	@AD1CON3 = (SAMPLE << 8) | CONVERSION
	@AD1CON2 = 0
	@AD1CON1 = 0x80e0 // auto conversion + turn on ADC
	@AD1CSSL = 0
endf
// *****************************************************************************
// sample selected channel and return value
// *****************************************************************************
function adc_get(channel)
	@AD1CHS = channel << 16
	@AD1CON1SET = 2 // start conversion
	while (peek(AD1CON1) & 1) = 0; wend
	return @ADC1BUF0
endf

function x()
	print adc_get(0)
endf

Load this project, type adc_init and then x. Each time you press x a conversion is done and the result shown on the screen. Rotate the trimmer and press x again for different results.

Some points to note:

The maximum reading should be 1023, you may get slightly less than this depending on the trimmer, it may not connect very well to the positive end, also there may be some loss in the wires. The reading is relative to the analogue reference (Vrefh and Vrefl). The is set in AD1CON2 and project_3 set this to be the power supply pins. For accurate conversions a voltage reference can be used. Set the trimmer to midway and press x a few times. Quite often you will not see the same reading twice. The ADC is accurate and it is reflecting what is on the pin. The point, is that the pin is infact a fluctuating voltage – only slightly but depending on what the sample is taken it may be on a high or low point. To counter this we normally take the average of several readings or place a capacitor on the input pin. This becomes more obvious in Project_3a.

Unfortunately it is beyond the scope of this tutorial to go into how the ADC works and all of the options that are available. If you simply want an analogue input then the code in Project_3 is good enough.

Input: Project 3a

http://byvac.com/mBlib/flb/Tutorial/PIC32MX1_Family/30_Input/Project_3a.bas

// Analogue input
//
// Registers
constant ANSELASET       0xBF886008
constant TRISASET        0xBF886018
constant AD1CON1         0xBF809000
constant AD1CON1SET      0xBF809008
constant AD1CON2         0xBF809010
constant AD1CON3         0xBF809020
constant AD1CSSL         0xBF809050
constant AD1CHS          0xBF809040
constant ADC1BUF0        0xBF809070
// adc
constant SAMPLE 5
constant CONVERSION 5
constant INP 1 << 0		// RA0

// *****************************************************************************
// Set up ADC for auto ample and scan
// *****************************************************************************
function adc_init()
	@TRISASET = INP // set as i/p
	@ANSELASET = INP // set as analogue
	@AD1CON3 = 0 // reset first, clock derived from PBCLK
	@AD1CON3 = (SAMPLE << 8) | CONVERSION
	@AD1CON2 = 0
	@AD1CON1 = 0x80e0 // auto conversion + turn on ADC
	@AD1CSSL = 0
endf
// *****************************************************************************
// sample selected channel and return value
// *****************************************************************************
function adc_get(channel)
	@AD1CHS = channel << 16
	@AD1CON1SET = 2 // start conversion
	while (peek(AD1CON1) & 1) = 0; wend
	return @ADC1BUF0
endf

function go()
dim j, avg
	adc_init()
	seg_start()  // get the LED display going
	while comkey?(2) = 0
		avg=0
		for j = 1 to 30
			avg = avg + adc_get(0)
		next
		num_disp4(avg/30)
	wend
endf

This project presents a great starting point for experimentation. It does show very clearly the practical problems of Analogue to digital conversion. Load and type go().

NOTE:
the ‘go’ routine contains the start up code for the LED (seg_start()), this should only be run once per reset as it adds the interrupt to the interrupt table. If you intend experimenting then move this out of the go function.

The value is now presented on to the LED display. Here are some problems to solve:

  • Why may there be a problem with this “num_disp4(avg/30)” line? (hint divide by 0)
  • Even with averaging the end digit on the display tends to fluctuate between readings. Increasing the number of averages is not the solution.

The answer to 2 is always dependant on the final application but it is more difficult to get an even display than might at first be imagined.