MPLAB X on PIC10F222, getting started. - Page 1
Forum section: electronics / general

author: step_s
author_rank: Regular Contributor
timestamp: December 10, 2015, 08:46:23 pm
content: Hi EEVblog.I've just started playing around with microcontrollers and i have some PIC10F222 i wanted to try and program.In my search I've watched some youtube videos, and different tutorials, but they are all different. Maybe it's due to the age of them.The problem is with the code, and even in the most basic of basics, I get nowhere.So I understand that TRIS is the register for the pins, and setting them to output or input, but I can't find the specific code for this.Some videos seem to write "TRISbits", "TRISB" or "TRISIO". None of these work.I'm completely lost, and there is very little info on MPLAB X IDE. . .Any suggestions, or examples of simple codes for this chip?Hope you can help.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: December 10, 2015, 10:06:12 pm
content: PIC10F222 doesn't have a TRISB or TRISIO register (it has TRISGPIO, but this is not directly accessible). tutorials may be written to suit different chips. You should read the datasheet to find out what registersyourPIC has.To set I/O directions you load the bits into W and then execute the TRIS GPIO instruction. Bits set to 1 make the corresponding I/O bit an input, while bits set to 0 are outputs (note: GP3 is input only). These bits are often combined in a constant called 'TRISbits' as a convenience and to (perhaps) make the code easier to read.

author: step_s
author_rank: Regular Contributor
timestamp: December 10, 2015, 10:25:42 pm
content: @BruceThank you for your answer BruceI've read the datasheet and found that it does have a TRISGPIO, but how is this written then?The datasheet says "TRISGPIO ---- 1111" how is this written in the C code?If i need the pins to be outputs, am i suppose to write it as hex, or how so?Thank you for your help

author: MarkF
author_rank: Super Contributor
timestamp: December 10, 2015, 10:58:14 pm
content: I found some sample code from simple led blink with pic10f222 and xc8 compiler.Code:#include <xc.h>/***** CONFIGURATION *****/__CONFIG(MCLRE_OFF & CP_OFF & WDTE_OFF & MCPU_OFF & IOSCFS_4MHZ );#define _XTAL_FREQ 4000000 // oscillator frequency for _delay()/***** MAIN PROGRAM *****/void main(){// InitializationADCON0 = 0; // GP0 and GP1 configured as digital I/OOPTION = 0b00000111; // If the T0CS bit is set to ‘1’, it will override the TRIS function on the T0CKI pinTRIS = 0b111000; // configure GP2 (only) as an output// Main loopfor (;;){GPIO = 0b000100; // turn on LED on GP2 (bit 1)__delay_ms(250); // stay on for 200 msGPIO = 0b000110; // turn on LED on GP2 (bit 1)__delay_ms(250); // stay on for 200 msGPIO = 0b000111; // turn on LED on GP2 (bit 1)__delay_ms(250); // stay on for 200 msGPIO = 0b000000; // turn on LED on GP2 (bit 1)__delay_ms(250);} // repeat forever}The PIC10F222 does NOT work like a typical PIC. I would recommend trying a different chip to learn on. Maybe a PIC16F886 or a PIC18F2550.

author: step_s
author_rank: Regular Contributor
timestamp: December 10, 2015, 11:25:12 pm
content: @MarkHi Mark, thank you for the post.The ADCON0 = 0; helped me to actually make the PIC work now.I'm afraid that the MPLAB X IDE is ridden with problems (or so i read everywhere).The delay function given in this example should work, by doesn't, due to . . . reasons.The userguide for the XC8 has a command _delay(40000); as an example, and this doesn't work either.Would another compiler be better, or?How is this PIC different from most others?Thank you.

author: Ian.M
author_rank: Super Contributor
timestamp: December 10, 2015, 11:39:02 pm
content: The PIC10F222 is a very ordinary baseline (12 bit core) PIC device. Its only noticeable quirk is that GP2/T0CKI defaults to being the T0CKI input and you must disable that to use it for ordinary I/O.N.B. As with any other PIC, to use analog capable pins GP0/AN), GP1/AN1 for digital I/O you must disable analog mode. See the ADCON0 SFR.Your best bet is to start from the Gooligum tutorials:http://www.gooligum.com.au/PIC-tutorials/baseline-PIC-tutorial(Free, but requires registration to download) which introduces the very similar (but less capable) PIC10F200 and the baseline PIC1xF50x family. You will then be able to understand the sample code MarkF posted.

author: MarkF
author_rank: Super Contributor
timestamp: December 10, 2015, 11:50:24 pm
content: Here is how I do my delays:Code:#define _XTAL_FREQ 20000000L#define delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))#define delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))MPLAB X is not my favorite editor but it gets the job done for me. I haven't had a major issues with it.

author: Ian.M
author_rank: Super Contributor
timestamp: December 11, 2015, 12:03:14 am
content: The delay defines are in the standard headers included by xc.h. The only one you need to provide is the _XTAL_FREQ define.Old versions of MPLAB X had a bug in the syntax hiliter's interaction with the XC8 toolsuite such that it incorrectly hilited the delay macros as errors, if you didn't either redefine them in your own code or use a workaround involving defining __PICC__ before #including xc.h. MPLAB X v3.15 + XC8 v1.35 have no such issue.

author: 0b01010011
author_rank: Regular Contributor
timestamp: December 11, 2015, 03:02:45 am
content: if you want a simple PIC to learn on but one that is a lot like others then the PIC10F322 is a good one, it has some more up to date peripherals, interrupts, etc.the 10F2xx isn't a good learning example as they are fairly unique, much unlike all the PIC10F3xx/PIC12/PIC16 series.

author: westfw
author_rank: Super Contributor
timestamp: December 11, 2015, 08:14:18 am
content: Early-architecture PICs (12bit-wide instruction words) had a TRIS instruction rather than TRISx registers (there are TRISx registers mentioned in the datasheet, but if you look carefully, they don't have any "address" and aren't writeable by the normal movwf-type instructions.When the 14bit PICs came along, the TRIS functions got moved to "regular" file register locations, and the TRISx instructions were deprecated.I have to agree with others that a bare-minimum PIC like the 10F series is NOT a good place to start for someone just starting to look at microcontrollers.(In a way, this illustrates a common progression of microcontroller architectures. Early microcontrollers, like the PIC and 8051, have a lot of their instruction set dedicated to manipulating the IO ports/pins. As you might expect, since they were explicitly designed to to do IO-like things. But newer microcontrollers start to favor more general purpose instruction sets, with fewer weird instructions dedicated to IO. By the time you get to a modern architecture like ARM, there are essentially no IO-specific instructions left at all.)

author: Ian.M
author_rank: Super Contributor
timestamp: December 11, 2015, 08:48:03 am
content: Most importantly, on 12 bit (baseline) PICS, the TRIS register(s) and OPTION register are WOM. You cannot read them back. You must therefore compose the required 8 bit value for all pins on the port or all OPTION flags and write the whole of it at one shot. The compiler cant help you with an easy way to set/clear one of these flags (e.g.TOSE).on its own, because the hardware cant do that.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: December 11, 2015, 12:19:45 pm
content: Quote from: step_s on December 10, 2015, 10:25:42 pmXC8 defines a special variable called 'TRIS'. When the compiler sees this it knows to produce the required code (see section 5.3.9 of the compiler manual for details). Allyouhave to do is write eg:-TRIS = 0b00001001; // bits 2 and 1 are clear, so GP2 and GP1 are outputsQuoteYou can write it any way you like so long as the resulting number has the correct bits cleared. What I usually do is name anyinputbits and set them like so:-#define INPUTX 0 // GP0, pin 1#define INPUTY 3 // GP3, pin 6TRIS = (1<<INPUTX)|(1<<INPUTY); // set input bits. All other bits are zero, and so will be outputs.This way you can quickly change a pin assignment without having to hunt down and change raw numbers in different parts of your code.

author: step_s
author_rank: Regular Contributor
timestamp: December 11, 2015, 12:58:53 pm
content: Quote from: MarkF on December 10, 2015, 11:50:24 pmHi Mark. So when you have defined the delays, how to you include them in to the main code?Are the "x" here just to tell the program that it's for the user to set at a later time?Quote from: Ian.M on December 10, 2015, 11:39:02 pmHi Ian. I just downloaded these, and they don't tell me a whole lot that i don't already know. They use the __delay_(ms) function, and this doesn't work. I have tried including delay.h and libpic30.h, but none of these will make the compiler run.P.S. I just made some kind of progress. . . #define FCY 1, the value here apparently has no real meaning since every number does the same. Then using the _delay(x) code, where the X seems to be the amount of µseconds. The combiler has a small "unable to resolve identifier _delay." but has no errors when running.Can anyone explain why this works?Thank you.

author: mariush
author_rank: Super Contributor
timestamp: December 11, 2015, 01:16:37 pm
content: I wrote these for another project of mine, and the C file has delays for 16 Mhz , 8 Mhz and 4 Mhz ... but I only tested thoroughly the 16 Mhz ..see attachmentThey probably work well only on PIC16 and newer, but you should check the datasheet to see how much time the instructions take to execute on pic10.. it could be they take the same amount of time.Basically, it's taking advantage of the NOP and GOTO instructions to make your code do nothing for a period of time. On PIC16, NOP takes one cycle, GOTO takes 2 so by chaining a few of these and taking in account the cycles it takes to enter and exit a function and the time it takes to do "for" or while .. you can make your program waste lots of time with just a few lines of code.

author: MarkF
author_rank: Super Contributor
timestamp: December 11, 2015, 03:06:55 pm
content: Quote from: step_s on December 11, 2015, 12:58:53 pmUsing the delays in code will look like:Code:void main() {while (1) {GPIO = 0b000100; // turn on LEDdelay_ms(100); // delay 100 msGPIO = 0b000110; // turn on LEDdelay_ms(200); // delay 200 msGPIO = 0b000111; // turn on LEDdelay_ms(300); // delay 300 msGPIO = 0b000000; // turn on LEDdelay_ms(400); // delay 400 ms}}The compilier will turn this into:Code:void main() {while (1) {GPIO = 0b000100; // turn on LED_delay((unsigned long)((100)*(_XTAL_FREQ/4000.0)))GPIO = 0b000110; // turn on LED_delay((unsigned long)((200)*(_XTAL_FREQ/4000.0)))GPIO = 0b000111; // turn on LED_delay((unsigned long)((300)*(_XTAL_FREQ/4000.0)))GPIO = 0b000000; // turn on LED_delay((unsigned long)((400)*(_XTAL_FREQ/4000.0)))}}Think of delay_us(x) and delay_ms(x) as function calls instead of defines. The difference is that every where in the code you place delay_us() or delay_ms() the complier will replace them with _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0))) or _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0))) respectively.The defined code will be repeated every where. So if you're short on memory, you might want to make them actual functions instead of #defines. The trade off is that you will save memory at the expense of execution time due to a function call.

author: Delta
author_rank: Super Contributor
timestamp: December 11, 2015, 03:24:05 pm
content: I got started with PICs using only the chip's datasheet and the assembler's user manual (only moved onto C later). I found this much easier than trying to work through various online tutorials...I find the Microchip datasheets very very good, if a bit verbose...

author: Ian.M
author_rank: Super Contributor
timestamp: December 11, 2015, 03:24:58 pm
content: RTFM. (the XC8 one). The compiler's own delay ms/us macros start with a double underscore and take a numeric constant as the parameter (never a variable or a C const). As long as you have defined _XTAL_FREQ before you use them and don't exceed the max delay limits for _delay() given in the manual in terms of instruction cycles (Fosc/4), they just work, and have ever since XC8 was first released.The *ONLY* header you need for most programs is:Code:#include <xc.h>If a library function needs another header included, the manual will tell you. This is not mainframe or PC hosted C and on a PIC10, you will *NEVER* need to include stdio.h, and scarcely ever stdlib.h.Be aware that baseline PICs only have a two level hardware stack. The compiler needs one stack level for reading const arrays using RETLW tables, so if you call graph has more than two levels if you use const strings or arrays, or three levels without such consts, the compiler will have to use a software stack, which will increase the code size. You cant afford that on a PIC10. Rule of thumb for baseline C coding: Apart from main(), don't write a function that calls another function you wrote!

author: step_s
author_rank: Regular Contributor
timestamp: December 11, 2015, 04:42:57 pm
content: Ah, thank you for explaining Mark.I figured as much, and i see that you are using the _delay(x) command that i got working, and which is a reflection of the clock frequency somehow.I guess it makes sense to do this trick in order to calibrate the counter to be more accurate, but the _delay(x) command uses less memory as long as you know what number you need to write.It's beginning to make some more sense now, but is still very confusing xDSo now that i have these things working, can you guys help me on the next problem?This chip needs to run a voltage indicator, so I need it to read the voltage on one pin, and save that to a string of sorts, so I can use the value elsewhere.The datasheet states that this chip will use the VDD as voltage reference, and that the analog input will then be converted to a value between 0 and 255, where 5V is supplied and will be 255.Any help on this one?

author: MarkF
author_rank: Super Contributor
timestamp: December 11, 2015, 05:06:43 pm
content: What voltage range do you need to sample? As long as it's less than Vdd, the voltage will be the following:Vsample = Vdd * (ADRES / 256)where ADRES is the Analog Conversion Result Register. See paragraph 7.8 of the pic10f222.If the voltage you want to sample is greater than Vdd, you will need to setup of a resistor voltage divider and then scale Vsample accordingly.All the information you need is in the data sheet. Keep re-reading it until you understand what's needed.

author: Ian.M
author_rank: Super Contributor
timestamp: December 11, 2015, 05:33:35 pm
content: IIRC the Gooligum baseline C tutorials includes using the ADC (lesson 7). You will have to tweak the ADCON 0 ADC setup a bit as you are using a different PIC, but the basics of selecting a channel (ADC input pin), triggering a conversion and reading the result from ADRES will be the same.If you get stuck, post your code in code tags, or attach it and if it wont compile, post the compiler error messages in code tags.

author: step_s
author_rank: Regular Contributor
timestamp: December 11, 2015, 06:14:16 pm
content: @Mark@IANI'm really trying to figure out the datasheet, combined with the Googligum tutorials, and setting it up so that the PIC works! xDCode:unsigned int ADCvalue=0;void main(void) {TRIS = 0b0001; //Set only GP0 to inputADCON0bits.ANS = 0b01; //Setting GP0 to analogADCON0bits.CHS = 0b00; //Select channel ANS0ADCON0bits.ADON = 1; //Enable ADCwhile (1){while (GP3 = 0) //When button is pressed{ADCON0bits.GO = 1; //Start ADC_delay(1000); //Delay to know ADC is doneADCvalue = ADRES; //Store ADRES}}}This is what I've got so far. This works in the compiler.The question is now: How do I use the ADRES or the ADCvalue as an integer value for, let's say, a delay?_delay(ADCvalue), something on these lines? Isn't the ADRES in binary? I would need it in decimals from 0 to 255.I want to to make the LED blink with a speed set by the voltage on the input, as a startThank you guys!

author: MarkF
author_rank: Super Contributor
timestamp: December 11, 2015, 06:33:11 pm
content: This is how I would do it:Code:unsigned int value;while (1) {// Read analog dataADCON0bits.GO=1;while (ADCON0bits.nDONE);value = ADRES;// Blink LEDGPIO = 0b000100; // Turn on LEDdelay_ms(value*4); // Delay 0-1 secGPIO = 0b000000; // Turn off LED}

author: step_s
author_rank: Regular Contributor
timestamp: December 11, 2015, 06:41:48 pm
content: Quote from: MarkF on December 11, 2015, 06:33:11 pmIt doesn't like that i stick the value directly into the delay. _delay(value) doesn't run.Isn't the ADRES in binary btw? How is it convertet?

author: MarkF
author_rank: Super Contributor
timestamp: December 11, 2015, 06:53:49 pm
content: Okay. It's probably because the #define is done at complie time instead of execution time.You will need to call the _delay function directly. Replace delay_ms() with _delay((unsigned long)((value*4)*(_XTAL_FREQ/4000.0))).The ADRES value is a number between of 0 to 255. This represents a voltage of 0 - Vdd volts on the input pin. There are a number things you can do:Convert the number to a voltage (Voltage = ADRES/256). ADRES / 256 is a fraction of the PIC supply voltage (Vdd).Use the ADRES number for some another function. In your case, you want a delay in milli-sec. So if you want a 1 sec delay you need a number of 0 to 1000 ms. Hence, multiply the ADRES (0-255) value by 4.

author: Ian.M
author_rank: Super Contributor
timestamp: December 11, 2015, 06:59:35 pm
content: @Mark: That wont work.@O.P.That is when you *NEED* to use a delay function or routine that will take a variable as a parameter rather than the compiler's built-in _delay() or __delay_us(), __delay_ms() macros. Mark's delay macros wrap _delay() so have the same 'no variables' limitation.You could use one of the variable delay functions for XC8/HiTech C you will find scattered all over the net (and if you want a particularly accurate one for PICs with more memory, ask me), however on a PIC10, I'd do it a bit differently, because of the limited stack depth, RAM and FLASH.Code:// Blink LEDGPIO = 0b000100; // Turn on LEDfor(i=0; i<ADRES; i++) __delay_ms(4); // Delay 0-1 secGPIO = 0b000000; // Turn off LEDishould be declared as unsigned char at the start of the main() function. As xc8 isn't officially C99 compliant yet, avoid the temptation to declare it in the for() statement. Don't forget the#define _XTAL_FREQ 4000000ULThere's another pitfall here. If you usei<=ADRES, the loop will never end if ADRES is 255, asiwill wrap to zero before it can become greater than ADRES.

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: December 12, 2015, 07:44:46 pm
content: Quote from: step_s on December 11, 2015, 04:42:57 pm_delay(x) simply executes a fixed number of CPU instructions which the compiler has pre-calculated will produce the required delay. It works because the PIC always takes the same amount of time to execute each instruction. Of course this timing is dependent on clock speed, so the compiler must be told what clock frequency the chip will be running at.Note that this delay is not accurate when put into a program, because other code in the loop adds an unknown number of extra instructions. A few lines of C code could add 10s to 100s of microseconds.

author: westfw
author_rank: Super Contributor
timestamp: December 12, 2015, 11:21:29 pm
content: QuoteQuoteI doubt that 100 microsecond accuracy is needed for LED blinking :-)Code::value = ADRES; // Get A-D value, 0-255// Blink LEDGPIO = 0b000100; // Turn on LEDwhile (value--) {delay_ms(4); // Delay 0-1 sec total. 4 ms for each A-D count.}GPIO = 0b000000; // Turn off LED

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 12:40:25 am
content: Quote from: Ian.M on December 11, 2015, 06:59:35 pmThank you for the post IanSo what you are saying is that the _delay(x) function is not able to load variables directly. . . I'm a bit confused in trying to find another macro on the net, since all I seem to find is pretty much what Mark has written. If you had a suggestion for putting in a variable directly, it would be much appreciatedQuoteSo basicly this code compares ADRES to the unsigned char, and if it's lower than ADRES, the char gets +1 and a 4ms delay is added. This will run until I = ADRES, and the LED wil light. Am i right? Is this a good way of doing it, or a necessary evil?Thanks in advance!

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 01:08:28 am
content: Its a good way of doing it on a PIC10 due to its limited resources, though you might also call it evil. On a 14 bit cire PIC18 or an 16 bit core PIC18, a carefully tuned delay function is better: Seehttp://www.microchip.com/forums/m877727.aspx

author: westfw
author_rank: Super Contributor
timestamp: December 13, 2015, 02:45:38 am
content: I would normally do:Code:void delay_ms(int n) // delay for a variable number of ms{while (n--)__delay_ms(1); // delay that requires a constant.}main() {:value = ADRES;:delay_ms(value);And only didn't because of the known limitations of the PIC10 (call stack, variable space, questionable amount of C compiler optimization.)(It would probably be "educational" to run several of these suggestions through your compiler and compare the assembly code produced.)(A "reasonably good" compiler would inline the function call and notice that "value" would be overlapped with n, and you'd get the same code.)

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 02:53:57 am
content: Code:do{ __delay_ms(1); }while(--n);Has less loop overhead provided n is unsigned char, as the loop compiles to a DECFSZ and a GOTO or BRA (under HiTech C or XC8), but it has the disadvantage that n=0 takes 256 loops, and the small overhead doesn't matter if you are running off a +/-1% to 2% internal oscillator.

author: MarkF
author_rank: Super Contributor
timestamp: December 13, 2015, 04:43:58 am
content: Quote from: step_s on December 13, 2015, 12:40:25 amUsing --delay_ms(1) inside a loop will be the best and easiest way to implement a delay in most cases. Another option would be to use one of the timers (if one is available). However this is quit involved. I would only use a timer (with or without interrupts) if you need a long delay or have special timing requirements.

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 12:10:21 pm
content: Okay guys, thanks for all the great answers!So after looking at your suggestions I have made the following code:Code:#define _XTAL_FREQ 80000000UL#define delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/40000.0)))unsigned char i;unsigned char j;void main(void) {ADCON0bits.ANS = 0b01; //Setting GP0 to analogADCON0bits.CHS = 0b00; //Select channel ANS0ADCON0bits.ADON = 1; //Enable ADCOPTION = 0b00000000; //Disable all OPTION to enable GP2TRISGPIO = 0b00001001; //Set GP0 and GP3 to inputwhile (1){GP1 = 0; //Resets LEDSGP2 = 0; //Resets LEDSwhile (GP3 = 0); //Button press{ADCON0bits.GO=1; // Read analog datawhile (ADCON0bits.nDONE); // Wait for A/D to converti = ADRES; // Transfers ADRES to ij = ADRES; // Transfers ADRES to jGP1 = 1; // Turn on RED LEDGP2 = 0; // Turn off BLUE LEDfor(i=0; i<ADRES; i++) delay_ms(18); // Delay = 1sec/vGP1 = 0; // Turn off RED LEDGP2 = 1; // Turn on BLUE LEDfor(j=255; j>ADRES; j--) delay_ms(18); // Delay = 1sec/v}}}I have a two colored blue/red LED connected, and this will make it blink depending on the voltage.By raising the divider in (_XTAL_FREQ/40000.0) to give a lower delay, I can make the LED blink with a speed that makes for an intermittent color red->purple->blue.This is pretty much what i wanted, but now i have two problems:How can I make the LED switching on/off into a separate delay, so that they will stay flickering in the intermittent color for a short while,after the button press? A loop inside a loop I would guess? I can't seem to find any PWM functions on this PIC, since this would cut off some code,and make me explore some new features + make for a better LED driver.Also, I need some sort of "interval" on the voltage, since this is for a lithium cell, and it will need the color to be red at 3V and go towards blue at 4.2V.I might be overlooking some simple things right now, but I just got up. . forgive me xD

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 12:36:08 pm
content: Its a baseline PIC so there are no interrupts, no PWM and if you want to do anything concurrent at different time scales(e.g. read a button and PWM a LED), your only choice is a state machine in a fastsuperloop.If you upgraded to a midrange device you get timer interrupts, so could do software PWM in the background, and in the case of the pin-compatible PIC10F320/322 chips, you also get two hardware PWM modules so can 'set and forget' the LED colour, and the hardware will maintain it while you code does other stuff.Edit: dead link switched to wayback machine archive

author: Howardlong
author_rank: Super Contributor
timestamp: December 13, 2015, 02:03:34 pm
content: You could use the watchdog timer to delay turn off, this is the closest you'll get to an "interrupt" on that device, but it resets, so at the beginning on main() you have to check to TO, PD and GPWUF bits of the status register to determine what caused the reset.All of this is fiddly and makes for difficult to read code. While it can and is done, I'd agree with Ian and jump up to the 10f320/322 if you need the same form factor device for the reasons he cites.Alternatively, stick with what you have, and if you get it to work reasonably well it will be a great learning experience. Sometimes, though, if there's no need to beat yourself with a stick, then don't!

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 02:32:37 pm
content: There's no need to beat yourself with several sticks.There are perfectly good baseline parts with a lot more pins if you want to learn assembler programming on the 12 bit core as an intellectual 'hair shirt' exercise: e.g. PIC16F527 which has 20 pins including a full 8 bit port C so you can output whole bytes to LEDs to debug it.e.g. if you are feeling flush, hang two N.O.S.TIL311Hex decoded 7 segment LEDs off it for direct readout. Me? I make do with binary readout on a 10 LED bar graph and a 330R resistor array.The highly pin constrained PIC10 parts should only be used when size and weight are ultimate requirements. Due to the low pin count it is often difficult to arrange the application circuit to permit in-circuit programming without adding a lot of extra components, so its usual to order them pre-programmed. Use of the PIC10 DIP packages other than for pure development is a sign of insanity, you might as well use a PIC12 and get two more I/Os, or free ICSP/ICD pins.The cost savings of using an under-specified chip are usually so small that the added development costs involved with torturing your programming team till the code fits wont be recovered till somewhere between 10K and 100K units have been sold. Don't do it, especially not for small volume and one-offs.

author: Howardlong
author_rank: Super Contributor
timestamp: December 13, 2015, 02:59:58 pm
content: All of what Ian.M stated.Even with the debug header versions of the very low end devices, debugging is painful, typically with only one breakpoint for example, so you can't single step and have a separate code breakpoint.As a general rule for these devices, I develop on a near identical device with a few more pins for debugging (blinky pins, and in circuit debugging/programming) and if necessary port back to the real thing.The only thing I use these the PIC10F2xx for has been to program up chip registers on power up like clock generators. For that they're great, but not for anything much more complex.

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 05:19:27 pm
content: Thank you all for the great advice. I just leaped in to this with no knowledge at all, so it means a lot!I see now that the chip is quite inadequate for the task I wanted it to do.Say I wanted to upgrade to a better suited PIC, and i wanted to make it in to a complete project for the lithium cell. Looking at the PIC16 family with 14 pins, would any of these be able to do the task of:Have the battery indicator formulated in this thread (Only 2 color LED version is needed).Be able to run a step up converter for lithium cell to 5V, at a reasonable frequency (500kHz-1MHz).Be able to run a step down converter for 5V to lithium cell.Have room for a few other button press features for some extra fun.My first thought was to just have a step down converter to the cell, to charge it from a 5V source, up to around 4V (since most converters have a 1-1.2V gap from input), and then have a linear chip take over for the last 0.35V. This was in order to prevent overheating when the battery voltage was low in the first part of charging (the batteries are 3200mah 4.35V cell).Then after that, have a step up converter with a good solid output, from the cell to a 5V output (already played around with some good SOT-23 1.5A ones).At the end have this small PIC10 run a voltage indicator. For a total of 3 x SOT-23 package chips, and a few other components.Maybe i should make a new thread on this, but any help is much appreciated!

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: December 13, 2015, 05:44:12 pm
content: Quote from: step_s on December 13, 2015, 12:10:21 pmDon't do this. The delay_ms() function will then not be milliseconds, but something else. If you want a shorter delay then change the value of x in delay_ms(x). If 1ms is too long then change the name of your macro to something more appropriate, eg. delay_us() or my_delay().Use a define so you only have to change the delay value in one place, like this:-#define TICK 18...for(i=0; i<ADRES; i++) delay_ms(TICK); // Delay = 1sec/vQuoteYes, just create an outside loop that repeats the blinking pattern a certain number of times after the button press.QuoteFirst get it to work properly, and don't get carried away trying to make it 'better' or adding features unless there is a real need for it.The first thing I would look at is ADC accuracy. To get a stable result you will probably have to take several readings and average them.QuoteSubtract 3V from the ADC reading (or make it zero if already less then 3V). If the analog input is scaled to get a reading 255 at 4.2V then you have 16.5mV per step. 3V/16.5mV = 182. Subtract 182 from the ADC reading and you get a range of 0-73 representing 3.0 to 4.2 Volts.Make your flicker routine loop 73 times, with a step delay of 1/73 = 13ms giving a 1 second flicker period (or use a shorter delay if you want it to flicker faster). When the loop count is less than or equal to the ranged ADC value turn the Blue LED on and the Red LED off, when it is above do the opposite.QuoteIt is quite adequate for what youwantedit to do.QuoteSome of the latest chips may be able to do all that, but do you have the programming skills to get it all working properly?QuoteThis is a large cell and potentially very dangerous. I would not consider using a PIC to charge it unless I was very sure that my code was safe.

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 06:08:48 pm
content: A MCP19111 has enough processing power (a 8MHz PIC midrange core) and a realtime analog synchronous buck control loop sufficient to implement LiPO battery charging and management with minimal external components apart from a pair of MOSFETs. However it cant directly handle boosting the LiPO to 5V unless you want to write a software driven boost converter and certainly cant do that at >500KHz so would need an external boost converter for the application output 5V regulator, although it could monitor and control one.It certainly isn't a 14 pin part, and as Bruce has pointed out, DIY LiPO management in software requires excellent programming skills and a high level of justified self-confidence. Many consultants would be cautious taking that project on. Its certainly *NOT* novice friendly.You are going about this the wrong way. The question shouldn't be "What midrange 14 pin PIC can do all this?", it should be "What PIC or other brand MCU can tie together chips or hardware modules to monitor and control the following tasks, while operating over the full voltage range of a LiPO battery and drawing negligible current when off/quiescent?" and "How is my choice constrained by the T.C.O. of the tools to develop for and debug in-circuit my shortlist of MCUs?". Finally, beware offeeping creaturitiscreeping featuritis!

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 06:34:48 pm
content: @BruceTrue, it does what I wanted it to do, basicly, and it has still been quite insightful. I understand. . . keep the code understandable. Keep ms in ms and us in us!How would this outside loop look like? The 3-4.2v range is done! ThanksIt does seem like too much work to program a PIC when the fact is that the step-up and step-down IC's can be had in SOT-23 packages (same size as the switching MOSFETs I would need anyway), and it is tried and tested. Reliability over all.@IanI get your point. What works best together, and how simple and effective can it be done. I will try to not catch featuritis xD

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 06:53:27 pm
content: Got a PICkit 3? If so I'd be looking at debug capable (without extra headers) enhanced midrange PIC16F1xxx parts in 14 pin DIP for development and 16 lead QFN for the final version. The 16 lead QFN package is only 4mm x 4mm which is no larger than a PIC12 SOIC-8 footprint. There are no other PIC families in such a small package that have *ANY* members with built-in debug support. Dedicating the ICSP/ICD pins to development that leaves you with 9 I/Os for your application and hopefully not *TOO* much feeping creaturitis. The internal band-gap references aren't that great so you'll probably have to calibrate the ADC.Beware: not all PICs with a band-gap FVR module can actually feed that to the ADC module as the +Vref internally. Check datasheet and errata *BEFORE* designing one in!

author: Howardlong
author_rank: Super Contributor
timestamp: December 13, 2015, 07:15:49 pm
content: I concur regarding the MCP19111, it seemed like a great part, but when I got into the nitty gritty with it wasn't quite so nice. For example, it talks about being able to deal with supplies/chargers with Vout > 3.6v by using a simple potential divider: it can't, if you want current limiting, unless you use an external current sense device, which you'll then have to somehow interface to the MCP19111 ISEN differential inputs, and a simple pair of potential dividers won't work. I lost a week or two of my life on that part.Doing buck/boost in not trivial. Start with buck, then move up.For measuring current beyond the voltage range of your device, typically you either measure it with reference to ground with a current sense resistor (and all the issues you then have with a semi-floating ground) or use an external current sense part.

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 07:44:46 pm
content: @IanGot a Sure PicKit 3 clone. The PIC16F1xxx series seem like a really nice, and small PIC in the SMD version. Sure has lot's of PWM capabilities.Talking about debugging and calibrating the ADC seems quite confusing right now, that i just learned these other new things!You have some kind of suggestion for the delay function of the button that Bruce was talking about? The external loop?@HowardlongI will try this out in the future

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 08:15:44 pm
content: Did you read the 'superloop' link I gave in post#33?To do the Batt status LED active delay from the button push, I'd implement a superloop with a sandwich delay of no greater than 100ms, (preferably in the 10ms to 20ms range), using a hardware timer.In the superloop it would have various tasks implemented as state machines so they can all execute and either do nothing or the appropriate action for that moment. Any delays used would be minimal and only for unavoidable bit-banging of serial bus protocols to control peripheral chips. If any task needed a delay, it would count passes of the superloop until the time for its next action. All tasks would return as near immediately as possible.So with that out of the way, you'd need the following:* ADC task, responsible for updating a Batt_voltage variable* Button task, responsible for monitoring and debouncing the button* LED task, responsible for lighting the LEDs in the correct pattern until its time to turn them off again.Its then a matter of choice whether you make each task monitor global variables and flags to determine when to run, or whether you minimise such interfaces by having a:* SYSCON task responsible for timing and deciding when to start/stop other tasks.On anything except a baseline PIC, I'd use one function per task. On a baseline PIC, if I couldn't trust the compiler to inline stuff properly, I'd use a compound statement (block enclosed by{ }) for each task, carefully commented just inside the {, directly in main().

author: MarkF
author_rank: Super Contributor
timestamp: December 13, 2015, 08:38:07 pm
content: Quote from: step_s on December 13, 2015, 12:10:21 pmA programming note here:Code:i = ADRES; // Transfers ADRES to ij = ADRES; // Transfers ADRES to jSetting i and j do nothing in your code. The first thing the "for" loops do is to set i and j to zero.Also, you don't need two separate variables for the two loops. You could use either i or j in both loops.Your "for" loops will initialize i and j to zero when entering the loop and incrementing them at the end of each pass through the loop.

author: owiecc
author_rank: Frequent Contributor
timestamp: December 13, 2015, 08:53:36 pm
content: I ordered a PicKit3 and got a dev board with PIC16F1459 on it. It has a USB port. You get programming templates for OS X, Windows and Linux (MLA) so you can easily make an app to connect to your PIC.

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 09:02:31 pm
content: While that is a nice little chip for lightweight I/O via USB, *NOONE* in their right minds uses an 8 bit or 16 bit USB capable PIC for applications that don't need a USB interface. There's no need to pay extra for the USB SIE, loose I/O pins to it, and get a crappier set of peripherals due to the die area it takes up. The *ONLY* excuse is for a hobbyist who is having to settle for what's stocked locally because they cant order internationally.(The equation's a bit different with PIC32 as the feature set you need may not be availabele *without* USB)

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 09:22:48 pm
content: Quote from: Ian.M on December 13, 2015, 08:15:44 pmI read it now and understand the concept, but fail in seeing how to implement it. I see the PIC has a TMR0 internal timer, but how do use this function?You want to divide all of the tasks in to separate tasks, but defining them or?Sorry, but I'm a bit confused@MarkYe, thanks for keeping an eye out. . I saw the stupidity myself

author: Ian.M
author_rank: Super Contributor
timestamp: December 13, 2015, 10:05:37 pm
content: Sticking with your PIC10F222 for the moment, configure Timer 0 for internal (Fosc/4) clock with a prescaler of /128 for 4MHz Fosc or /256 for 8MHz Fosc. That means that starting from zero, the top bit of timer 0 will toggle every 16.4ms (aprox). Unfortunately there is no overflow flag for the timer so we cant do it the way we would on a non-baseline PIC.The superloop and sandwich delay code becomes:Code:while(1){TMR0=0; // Start sandwich delay: 16.384ms (nominal) 'ticks'// tasks go herewhile(!(TMR0&0x80)); // Wait for TMR0 top bit to end sandwich delay}The LED task goes something like:Code:{ // LED blinking taskstatic unsigned char LEDtime=0; // LED timer in complete flickersstatic unsigned char LEDcnt=MAXVOLTS; // LED counter for flickerif(button) LEDtime=5; // five complete flicker cycles per button pushif(LEDtime) {LEDtime--;if(LEDcnt>BattVolts) GPIO=RED; else GPIO=BLUE;if(!LEDcnt--) LEDcnt=MAXVOLTS; //reset flicker counter}}and of course you'll also need #defines for the bits to output for RED and BLUE and the scaled and offset ADC count MAXVOLTS corresponding to 3.4V on the LiPO.Bugfixed: missing;in if() ... else ...and corrected the TMR0 prescaler ratios.

author: step_s
author_rank: Regular Contributor
timestamp: December 13, 2015, 11:19:05 pm
content: Thanks on the code, but I'm afraid it's not really making sense to me. .Quote from: Ian.M on December 13, 2015, 10:05:37 pmSo i have set the prescaler to 1:256 (max value, should give longer time?), but the last bit: while(!(TMR0&0x80)); what does the ! and the & do?QuoteThis entire bit went completely over my head. .Sorry for the n00bishness Ian :/

author: westfw
author_rank: Super Contributor
timestamp: December 14, 2015, 12:24:23 am
content: Irrespective of all the other comments:Code:while (GP3 = 0); //Button pressThat while loop will never execute. You need an "==" instead of the "=" !

author: Howardlong
author_rank: Super Contributor
timestamp: December 14, 2015, 12:39:15 am
content: Quote from: westfw on December 14, 2015, 12:24:23 amThere are those who have, and those who will!

author: Ian.M
author_rank: Super Contributor
timestamp: December 14, 2015, 12:59:09 am
content: If you *don't* have a copy of K&R 2nd ed, you can get by withThe C Book, 2nd ed. by Banahan, Brady & Doran. Its not as well organised but it covers the essentials fairly thoroughly.Turn to the sections on logical and bitwise operators (2.6 and 2.9 in K&R, 3.3 and 2.8.2.3 in The C Book) and lookup the bitwise AND and logical negation operators. You will now be equipped to dissectCode:while(!(TMR0&0x80));.* First note the trailing;, this while() loop has no 'payload' - all it does is spin on the spot while its condition is TRUE. The comment 'Wait for ....' confirms that is my intention.* Now look in the innermost bracket.Unfortunately XC8 doesn't define a bitfield for the highest bit of Timer 0 and, unlike any PIC with interrupts, there is no flag set when it rolls over.We need a bitmask to extract the highest bit. The mask is 0x80 and after ANDING with Timer 0 we have a value that is either 0 0r 0x80.* However the requirement is to keep looping till that bit sets, so outside the inner brackets is the negation (NOT) operator. That converts 0x80 (or any other non-zero value) to 0 (FALSE) and 0 to 1 (TRUE).XC8 is fairly smart, and even the free mode recognises this idiom as a test and loop on a single bit and optimises it to:Code:85 1FB l392:86 ;main222.c: 6: while(!(TMR0&0x80));8788 1FB 7E1btfss1,7;volatile89 1FC BFBgotol392If I'd used the conceptually much simpler:Code:while(TMR0<0x80);it would use far more instructions as it would need to load the constant into W, compare it with TMR0 and then test a STATUS flag to decide what to do:Code:85 1F9 l392:86 ;main222.c: 6: while(TMR0<0x80);8788 1F9 C80movlw12889 1FA 081subwf1,w;volatile90 1FB 703skipc91 1FC BF9gotol392You really have to grok the instruction set to get the best out of XC8 on these tiny devices.The next chunk had a mistake, a missing semicolon in the if() ... else ...:Code:{ // LED blinking taskstatic unsigned char LEDtime=0; // LED timer in complete flickersstatic unsigned char LEDcnt=MAXVOLTS; // LED counter for flickerif(button) LEDtime=5; // five complete flicker cycles per button pushif(LEDtime) {LEDtime--;if(LEDcnt>BattVolts) GPIO=RED; else GPIO=BLUE;if(!LEDcnt--) LEDcnt=MAXVOLTS; //reset flicker counter}}The twostatic unsigned charvariable declarations declare local variables that are initialised on startup then retain their values from one invocation of the task block to the next.if(button) LEDtime=5;waits for the flag button to be set by the button task, it then sets the flicker cycke count to five. The button task will clear the flag on the next pass so this only happens once per debounced press. Alternatively you could let the flag stay set as long as the button is held and the LED task would keep doing the flicker pattern.if(LEDtime) {LEDtime--;checks LEDtime and if TRUE, decrements it to count down flicker cycles remaining.if(LEDcnt>BattVolts) GPIO=RED; else GPIO=BLUE;turns on the red LED if the flicker counter is greater than the battery voltage otherwise it turns on the blue LED. It also turns off the other colour LED at the same time. I'm assuming there are only two output pins and each LED is on one of them.Bugfix: inserted;before else.if(!LEDcnt--) LEDcnt=MAXVOLTS; //reset flicker countercounts down for the flicker level and when it wraps around from zero, does what it says and resets it to MAXVOLTS, which Bruce suggested should be 73 in post#38.

author: step_s
author_rank: Regular Contributor
timestamp: December 14, 2015, 02:53:23 am
content: Thank you for explaining Ian. Much appreciated!When I'm done with my psychology exams, I will pick up a few books on thisFrom these last posts, I have made the following code:Code:#define _XTAL_FREQ 80000000UL#define delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/400000000.0)))#define delay 1000unsigned char i;void main(void) {ADCON0bits.ANS = 0b01; //Setting GP0 to analogADCON0bits.CHS = 0b00; //Select channel ANS0ADCON0bits.ADON = 1; //Enable ADCOPTION = 0b00000110; //Enable GP2, & set TMR0 rate 1:128TRISGPIO = 0b00001001; //Set GP0 & 3 to input, GP1 & 2 to inputwhile (1){static unsigned char LEDtime= 0; // Char that stays codedGP1 = 0; // Resets LEDSGP2 = 0; // Resets LEDSif (GP3 ==0) LEDtime=252; // Button pressif(LEDtime) // If LEDtime = 0{LEDtime--; // Decrements LEDtime with 1ADCON0bits.GO=1; // Read analog datawhile (ADCON0bits.nDONE); // Wait for A/D to convert// LED color setting, fast switchingGP1 = 1; // Turn on BLUE LEDGP2 = 0; // Turn off RED LEDfor(i=160; i<ADRES; i++) delay_us(delay); // Delay, 3.2V refGP1 = 0; // Turn off BLUE LEDGP2 = 1; // Turn on RED LEDfor(i=205; i>ADRES; i--) delay_us(delay); // Delay, 4.2V refGP1 = 1; // Turn on BLUE LEDGP2 = 0; // Turn off RED LEDfor(i=160; i<ADRES; i++) delay_us(delay); // Delay, 3.2 V refGP1 = 0; // Turn off BLUE LEDGP2 = 1; // Turn on RED LEDfor(i=205; i>ADRES; i--) delay_us(delay); // Delay, 4.2V refif (ADRES<155) //If V<3.1{ // Blinks red 4 times if under 3.1VLEDtime=LEDtime-62; // Decrements LEDtime with 62GP2 = 1; // Turn on RED LEDdelay_us(6000000); // 1sec delayGP2 = 0; // Turn off RED LEDdelay_us(6000000); // 1sec delay}}}}As it says in the descriptions. This will show blue at 4.2V, shift to purple, and end at red at 3.2V, for 2sec when button is pressed. If the battery voltage drops below 3.1V, the LED will blink red 4 times instead.The LED flicker part with the delay from ADRES is pasted twice, for an easy way to double the time shown.Please let me know if anything looks silly. Even though this works, that doesn't mean that there isn't room for improvements

author: Bruce Abbott
author_rank: Frequent Contributor
timestamp: December 15, 2015, 07:44:37 pm
content: Quote from: step_s on December 14, 2015, 02:53:23 amIt works, but your code is a bit hacky and hard to follow, and may easily break if you have to change it. You can improve readability by converting 'magic' numbers to descriptive identifiers and letting the compiler do the grunt work.First thing I would do is assign each LED to its GPIO port with a #define, like this:-#define RedLED_ON GP2=1#define RedLED_OFF GP2=0Then, instead of having to add a comment to remind you what 'GP2 = 1' does, you can just do this:-RedLED_ON;It's a bit more effort initially, but makes the code more readable and will save a lot of time if you ever need to change it. Also you avoid introducing bugs due to not changing all instances.Avoid putting comments in places where they aren't necessary, and make sure they match the code! For example,delay_us(6000000); // 1sec delayis obviouslynota 1 second delay.On the other hand, liberally comment code whose operation is not obvious. It took me a while to figure out how you got the Red LED to reliably blink 4 times on low voltage. You subtract 62 from the count and stop when it gets to 0, which only works because it was previously decremented in the fast switching code and the initial count (252) is an exact multiple of 63.LEDtime=LEDtime-62; // Decrements LEDtime with 62...is a useless and misleading comment. That it subtracts 62 is obvious, butwhy62? Also the code will break if you change LEDtime and don't adjust this line to match.I would do something like this:-#define BLINKTIME 63 /* count to subtract from LEDtime when blinking Red LED on low voltage */if (GP3 ==0) LEDtime = BLINKTIME*4; // set count to keep showing LED display after button released...LEDtime = LEDtime - (BLINKTIME-1); // -1 to account for decrement in fast switchingOr, change the algorithm it so that on low voltage it avoids the fast switching code altogether. That way you can adjust the timing of each function separately and don't have to worry about interaction between them.