tterev3's PIC quickstart guide

Sweet :D

I see exactly how the voltage sampling works. Every 100 milliseconds it resets the timer (so it can count back up to 100 again) and sets the v_sample 'flag' to 1. Now that the v_sample 'flag' is 1, the trips if statement in the while loop inside the main function, which causes it to read the ADC result and change the PWM output accordingly (via the initialize_mode function of course). When the if statement is tripped, it resets the v_sample flag back to 0, which stops the if statement from being tripped until 100ms is up again.

So the delayms function you created won't necessarily save much space in this code (only called once), but it would if the delayms function was called more than once. Gotcha.

Seems I'm well on my way :) Thanks!

- Matt

EDIT: just remembered a note I made in the code, down in the configuration function. PWM1DCH = 0; is this effectively setting the PWM to zero (off)? At the start of the code you make pwm = PWM1DCH. Actually, you #define PWM PWM1DCH. What does that mean exactly? Does that just mean wherever you see 'pwm' you are actually seeing 'PWM1DCH'? So if I ever make PWM = something, I'm actually changing the value of PWM1DCH, and thus changing the PWM value being output?

I ask because I'm not sure if this is valid:

case batt_save: //battery saver case

pwm--; //reduce current pwm value by one

break; //force exit from statement body

In my code, if enter the voltage read loop (v_sample flag has been set to 1) it sets the current mode to "batt_save", and then executes the initialize_mode function. In the initialize_mode function, the above code is an additional case to the standard max, med, low cases. In theory it should take the existing pwm value and reduce it by one. The idea is that if you leave the light on, every time it detects the battery voltage has fallen below 3V it will reduce the PWM value by one. This will happen up to 10 times a second and in theory will continue to do so until the load on the cell is small enough that the voltage can rise back up above 3V. I will probably have to experiment with this and reduce it by more than 1 if it takes too long to react (and figure out how to reduce it in lots of 5 or so). This should work regardless of what mode you are in. If it cannot get it back above 3V, then it will simply turn PWM all the way down to zero, effectively turning the light off. This will protect your battery ;)

Preprocessor command. Any line starting with #define or #include or other similar commands will effectively modify the source code in place before sending it off to the compiler.

Using “#define foo bar” means that it should replace all instances of “foo” with “bar” before attempting to compile. It’s literally just an automatic search and replace. It’s handy for a lot of different things, like adding a layer of abstraction which doesn’t actually show up or take any space in the resulting binary after compilation. Hard-coding things in a manner which is easier to maintain, or providing tweakable compile-time parameters.

It’s a normal convention to use all-caps for #defined symbols, to avoid confusing them with variables or accidentally doing weird things to the code.

Another newbie question. I don't know about tterev3's code, but I think all the open-source, momentary-switch FW being used on BLF "turns off" to light by setting PWM to zero. Wouldn't there be less parasitic drain if the MCU was instructed to go into a stand-by or sleep type mode? I'm sure the answer to that is "Yes" based on the datasheets. So I guess the real question is the reduction in parasitic drain so small that it doesn't justify the complications (if any other than coding) that would be added by such a feature.

Thank you all and especially tterev3 for your patience with layman questions.

Yes, as ToyKeeper explained, this just tells the compiler that “pwm” actually means “PWM1DCH.” The reason I do this is so that if you wanted to use the other pin on PWM2, you could change just the one line to define “pwm” as “PWM2DCH” and all the functions that access “pwm” would be instanlty updated to use the other channel.

Hi Matt,
Your idea is good and will work with one slight adjustment: if you have just the line
pwm—;
when the battery gets very low and the pwm value reaches zero but battery is still below 3.0V, the decrement will roll the value back around to 255 (maximum brightness), which is the opposite of what you want in this case. To fix that, simply change the line to
if(pwm) pwm—;
which will only decrement if the value is not zero. If you wanted to build on this further, you could include a minimum level that the battery saver function wouldn’t go below:
#define minimum 5 //set value here to whatever you want (including zero)
if(pwm>minimum) pwm—;

hi ImA4Wheelr,
You’re right, the drain is much much less in sleep (generally a few microamps compared to about a milliamp when running) and it’s crucial that sleep mode is used on lights like this. I always use sleep mode when the light is off on things like my SkyRay and MELD2

Hi guys, Here's an adaptation of the same code that uses a momentary switch instead of power cycles to demonstrate debounce and sleep mode. With this framework you could do a lot more with length of switch presses, etc. by adding a press timer and watching the "pressed" flag. currently using 56% of flash memory and 31% of RAM

//v0 5/1/2014 by Everett
    //initial version
    //simple flashlight controller. mode change on power cycle
//v1 5/3/2014 by Everett
    //adapted to momentary switch

#include <htc.h>
#include <pic10f322.h>
#define _XTAL_FREQ 8000000
__CONFIG(CP_OFF & BOREN_OFF & LVP_OFF & MCLRE_OFF & WDTE_OFF & PWRTE_OFF & FOSC_INTOSC & WRT_HALF);

#define pwm PWM1DCH

unsigned char mode;
enum mode{
max=0,
med=1,
low=2,
off=3,
};
#define max_mode 3
#define default_mode 0

unsigned int v_timer;
bit v_sample;
#define voltage_rate 100 //milliseconds

bit pressed;
bit new_press;
char switch_count;

void delayms(int milliseconds);
void configure(void);
unsigned char read_voltage(void);
void initialize_mode(void);
void debounce(void);
void shutdown(void);

void interrupt isr(void)
{

if(TMR0IF){ //fires at 1kHz
    TMR0IF=0;
    debounce();
    if(++v_timer==voltage_rate){v_timer=0; v_sample=1;}
}

}

void main(void)
{
configure(); //set up hardware peripherals

mode=default_mode;
initialize_mode();  
pressed=0; new_press=0; switch_count=10;
GIE=1;  //turn on interrupts

while(1){
    
    if(v_sample){
        v_sample=0;
        if(mode==max){              //if battery goes below 3.0V in max mode, force down to medium mode
            if(read_voltage()&gt;87){
                mode=med;
                initialize_mode();
            }
        }
    }   
    
    if(new_press){
        new_press=0;
        mode++;
        if(mode&gt;max_mode) mode=0;
        initialize_mode();
    }
    
    if(mode==off)
    {
        shutdown(); //shutdown will lock up here until a press wakes the device
    }               
    
}   

}

void shutdown(void)
{
INTCON=0b00001000; //interrupt on pin change only
PWM1DCH=0; //zero output
PWM1CON=0; //turn off pwm
LATA=0; //ensure pin is low
FVRCON=0; //fvr off
ADCON=0; //adc off

while(1)    //make this a loop so we stay here until sure the switch went down
{
    do{
        debounce();
        delayms(1); 
    }while(pressed);    //ensure switch is up
    
    IOCAP=0; 
    IOCAN=0b00001000;   //interrupt on fall of ra3
    IOCAF=0;    //clear flags

    SLEEP();
    
    pressed=0; switch_count=10;
    for(char i=0; i&lt;40; i++){    //watch for up to 40ms for a solid press
        debounce();
        delayms(1);
        if(pressed) break;  //if pressed break out of for loop
    }
    if(pressed) break;  //if pressed break out of sleep loop
}       

configure();    //set up hardware for operation
GIE=1;  //turn on interrupts

}

void debounce(void)
{
static char port_copy=0xff;
#define switch_mask 0b00001000 //this selects RA3 as the switch

if((PORTA&amp;switch_mask)==port_copy)  //if the current state matches previous state
{
    if(--switch_count==0)   //count down samples. if 10 consecutive samples matched
    {
        switch_count=10;    //reset sample counter
        if(PORTA&amp;switch_mask) pressed=0;    //if the state is high, switch is up
        else        //else switch is down. check for new press
        { 
            if(pressed==0) new_press=1; //if last state of pressed was 0, this is a new press
            pressed=1;  //switch is now down
        }
    }
}
else    //state doesn&#39;t match, 
{
    switch_count=10;    //reset sample counter
    port_copy=(PORTA&amp;switch_mask);  //get new sample
}

}

void initialize_mode(void)
{
switch(mode){ //initialize current mode
default:
case max:
pwm=255;
break;
case med:
pwm=25;
break;
case low:
pwm=1;
break;
case off:
pwm=0;
break;
}
}

unsigned char read_voltage(void)
{
//fvr is at 1.024V. ADRES = 1.024/Vin*255. Vin = 1.024/(ADRES/255). voltage limit set to 3.0V -> 87. values below this correspond to voltage above 3.0V
GO_nDONE=1;
while(GO_nDONE);
return ADRES;
}

void delayms(int milliseconds)
{
while(milliseconds!=0){ __delay_ms(1); milliseconds–;}
}

void configure(void)
{
INTCON=0b00100000; //tmr0 only

T2CON=0b00000100; //on, no prescale
PR2=255; 
TMR2=0;
    
LATA=0;
TRISA=0b11111110;   //GP0 output
ANSELA=0b11110000;  //
WPUA=0b11111110;

OSCCON=0b01100000;  //8MHz

OPTION_REG=0b00000010;  //8 prescale for 1ms interrupts

FVRCON=0b10000001;  //1.024v to adc
ADCON=0b10111101;   // fvr

PWM1DCH=0;
PWM1CON=0b11000000; //on, output

}

Thank you for answering my question and for giving us more, what looks to my layman eyes, amazing code. I look forward to figuring out how it works and implementing it in my lights.

Thanks tterev3. You're a champ! Finding out about this PIC10F device and having this basic, but highly expandable code provided is seriously making me consider revisiting a few projects. Too many projects in fact lol. One at a time though. I get distracted far too easily haha.

Attiny10 don’t have the EEPROM. Can he do the same job here as PIC10F322?

The link in the OP for the XC8 compiler is broken. For anyone wondering, here is the download link for the XC8 compiler:

It's on the left hand sidebar under Downloads > XC8 > Windows.

EDIT: other than forgetting to define my batt_save mode, it compiled without issues. Now I just play the waiting game for my PICs to arrive :)

Yes, the Attiny10 has very similar specs to the 10F322. I have used both parts successfully for applications like this

Thanks for your prompt reply.
I am a dummy to program the MCU. I would like to ask if I define PB0 for tactile switch or touch switch, PB1 for PWM output and PB2 for voltage monitor ADC of the attiny 10 is on the right way?

The pin configuration depends on your software. The software can define the pin functions in many different ways to fit your needs. Are you writing software yourself or using existing code? Are you trying to make it work with an existing PCB or making a new one?

I am thinking to try writing my own code and am learning from the existing code. The PCB will be a new one. The reason for that pin configuration is that except VCC, GND and RST. There have 3 I/O pins left. All of the 3 pins can be defined for ADC. The OC0A and OC0B are located at PB0 and PB1 which seem to be a good choice for PWM and Q-Touch. If I am wrong, please correct me. I am really don’t know how to write program.
I am appreciated that you can give me some examples to follow. I noted that the attiny10 is quite different from the 13A and other tiny series. For example, it is 8-bit ADC and 16-bit Timer/Counter. Open source C code for attiny10 is difficult to find.

Hi Microa,
Since this thread is devoted to PIC, I started another one to show an example implementing the same thing on ATTiny10:
ATTiny starter code

Hey guys I need some help, remember I’m pretty much computer illiterate…

I have MPLAB X installed along with the XC8 compiler, I even have it opened up and the chip I want to use selected (12F1822) but that’s as far as I can make it, step 7. In what places do I add the HTC.H and P12F1822.h files? I just threw them in there randomly and pressed build and it actually did something (that surprised me lol) but didnt work, what do I do?

make[2]: * [build/default/production/D10 v1.obj] Error 1
make[1]: * [.build-conf] Error 2
make: * [.build-impl] Error 2

BUILD FAILED (exit value 2, total time: 719ms)

I can’t be of much help, MPLAB X sucks so I still use 8. I think you can get away with ignoring the include files and not linking them in, not sure though. In my opinion your best option is to uninstall X and get 8

I second tterev3's statement. I'm using MPLAB 8 IDE because when I tried X is was just stupid.

8 is older so there is a lot more user generated support material out there so its a lot easier to find answers via Google. The last release of MPLAB 8 was 8.92 and is available here: http://www.microchip.com/pagehandler/en-us/devtools/dev-tools-parts.html

I can tell you how to do everything you asked about for X....but in 8 lol.

- Matt

Ok guys got X removed and 8 installed (note if you install X first you will also have to remove XC8 as well, the version of XC8 installed with X wont work with eight, [stupid smileys] ), I see how you add the files now, thats much simpler however my first attempt I still get an error. I'm trying to use code tterev3's blog (his D10 code) on a 12f1822, I added the htc.h and the PIC12F1822.h files as well as the source code I d/l'd from his blog site, its a .ASM file, do I need to change its extension?

edit using V8.92 and XC8 V1.32 on a vista 64bit machine.

Build C:\Users\Dan\Documents\MPLAB\D10\D10 for device 12F1822
Using driver C:\Program Files\Microchip\xc8\v1.32\bin\xc8.exe

Make: The target "C:\Users\Dan\Documents\MPLAB\D10\D10 v1.p1" is out of date.
Executing: "C:\Program Files\Microchip\xc8\v1.32\bin\xc8.exe" -C "C:\Users\Dan\Documents\MPLAB\D10\D10 v1.ASM" -q --chip=12F1822 -P --runtime=default --opt=default -N-1 -D__DEBUG=1 -g --asmlist "–errformat=Error [%n] %f; %l.%c %s" "–msgformat=Advisory[%n] %s" "–warnformat=Warning [%n] %f; %l.%c %s"
Error [141] C:\Users\Dan\Documents\MPLAB\D10\D10 v1.ASM; 5.24 can't open include file "p12F1822.inc": No such file or directory
(908) exit status = 1

********** Build failed! **********

Looks like you have the wrong files included. If you look at the top of the source file, you can see the list of what you need to include. They are:

    #include    <p12F1822.inc> ; processor specific variable definitions
    #include    <instructions.inc>

Don't mix up the .h and the .inc, and don't add anything like htc.h (that is for a C compiler, this is written in assembly so you're not using the compiler). If you don't have my instructions.inc macro file, you can get it here