Si no me equivoco, o mal recuerdo la funcion milis() hace uso de un timer del Atmega para generar la demora esa, nomas que acostumbrado a la abstraccion que ofrecen las herramientas de Arduino aqui no es tan simple. Aca se podria hacer algo parecido por no decir casi identico, lo unico que deberias tener en cuenta son las fracciones, ahi el Arduino en su codigo lo tiene en cuenta a eso. Todo va a depender de la frecuencia de reloj que tengas.
Lo que podes hacer es uso de un Timer del PIC, ese PIC posee 3 timers:
Un Timer0 de 8 bits
Un Timer1 de 16bits
Un Timer2 de 8 bits
El Timer1 por tener mas bits te va a permitir hacer una interrupcion de mas tiempo. Ademas si usas el modulo CCP vas a hacer que se recargue solo, luego en la interrupcion vas a poder guardar tu numero
Supongo que milis() te da el numero de milisegundos que paso desde que se encendio el micro. Si es asi, esto seria un equivalente:
unsigned long msTicks;
void main() {
//Aca configuras tu Timer + CCP si es que lo vas a usar
while(1);
}
// Esta va a ser tu interrupcion, segun el compilador es distinto como se escriba la funcion
// Caso CCP, si usas solo el Timer vas a tener que recargarlo.
void Timer1() {
borro_flag_de_interrupcion();
msTicks++;
}
unsigned long milis(){
return msTicks;
}
Como configurarlo? nuevamente va a depender del compilador que estes usando, algunos poseen funciones para cargarlo mas "amistosamente" con una funcion, XC8 posee una libreria pero tambien podes hacerlo registro a registro, CCS posee una funcion, etc. Lo bueno es que si buscas el datasheet del micro seguro que en alguna parte te dice como habilitarlo, que bit cambiar, etc. incluso al final de cada seccion del modulo en el PDF, te indica los registros involucrados. En el datasheet tambien explica bit a bit cada uno los registros.
Tambien hay calculadoras online que te dicen que valor cargar. Si usas el timer solo va a ir en complemento a 2 el valor. Y si usas el CCP se compara con el valor final, asi que el valor va directo.
Por ahi segun la frecuencia que tengas, por ejemplo si usas un cristal de 4Mhz nomas, podes llegar a hacer interrupciones de hasta 0.5s lo cual en ves de estar preguntando por eso cada 0.0001s lo haces en mas tiempo.
Otra de las cosas que tenes que tener en cuenta. que cada instruccion del PIC lleva 4/Fosc en tiempo, asi que si tenes un cristal de 4Mhz tenes unas 1000 instrucciones para ejecutar entre cada milisegundo (que si usas un unsigned long + la interrupcion descontale unas 100) . Si aumentas la frecuencia obviamente tenes mas. Todo va a depender que necesites hacer. Y el compilador usado.
PD: Lo que buscas es simple, nomas que nadie lo hizo por que creo es desperdiciar un timer y tiempo de micro, obviamente a mi gusto. A no ser que realmente necesites medir o modificar algo en ese intervalo de tiempo. Sino prefiero hacerlo en el tiempo que yo requiera o si debo esperar por ejemplo 10 segundos, intentaria hacer la interrupcion lo mas larga posible, supongamos 0.5 y contar 20.
EDIT:
Encontre el codigo del Arduino, obviamente no posee la parte de configuracion
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds (a) ( ((a) * 1000L) / (F_CPU / 1000L) )
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)
SIGNAL(TIMER0_OVF_vect)
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}
unsigned long millis()
{
unsigned long m;
uint8_t oldSREG = SREG;
// disable interrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
m = timer0_millis;
SREG = oldSREG;
return m;
}
Por si queres guiarte de ese. Aunque tenes que tener cuidado con eso, a veces milis() salta de a 2 por culpa de su "fraccion"