imrishabh18/pedometer

This code defines and assembles a simple radio receiver hardware circuit using specific imported capacitors, inductors, RF connectors, and oscillator components with precise footprints and schematic attributes.

Version
1.1.3
License
unset
Stars
0

firmware/pedometer.c

#include "pedometer.h"
#include <string.h>

static bool wr(uint8_t addr,uint8_t reg,uint8_t value) {
 uint8_t b[2]={reg,value}; return board_i2c_write(addr,b,sizeof b);
}
static bool rd16(uint8_t addr,uint8_t reg,uint16_t *value) {
 uint8_t b[2]; if(!board_i2c_read_reg(addr,reg,b,2)) return false;
 *value=(uint16_t)b[0]|((uint16_t)b[1]<<8); return true;
}
static bool pmic_wr(uint8_t reg,uint8_t value) {
 uint8_t check=0; board_gpio_write(PIN_PMIC_LP,true); board_delay_us(2000);
 bool ok=wr(ADDR_BQ25150,reg,value) && board_i2c_read_reg(ADDR_BQ25150,reg,&check,1) && check==value;
 board_gpio_write(PIN_PMIC_LP,false); return ok;
}
bool pmic_init(void) {
 /* R11 makes this safe while MCU is in reset. LSLDO defaults ON at 1.8 V! */
 board_gpio_write(PIN_DISPLAY_ISOLATE,true);
 /* LDO off at 3.3 V, 4.20 V float, 2.5 mA precharge, 20 mA fast, 10% term.
  * Keep NTC active, watchdog off, 12-hour charging safety timer (never disabled).
  * Defaults are 10 mA if the PMIC resets. R3=499 ohm limits the charge code. */
 return pmic_wr(0x1d,0x6c) && pmic_wr(0x12,0x3c) && pmic_wr(0x14,0x02)
  && pmic_wr(0x13,0x10) && pmic_wr(0x15,0x14) && pmic_wr(0x17,0x94)
  && pmic_wr(0x36,0x04); /* PG reflects MR at 2.5 V; long press remains HW reset. */
}
bool display_power(bool on) {
 board_gpio_write(PIN_DISPLAY_ISOLATE,true);
 if(!on) return pmic_wr(0x1d,0x6c);
 if(!pmic_wr(0x1d,0xec)) return false;
 board_delay_us(100000); /* OLED auto-reset and supply settle. */
 board_gpio_write(PIN_DISPLAY_ISOLATE,false); board_delay_us(1000);
 const uint8_t init[]={0x00,0xae,0xd5,0x80,0xa8,0x1f,0xd3,0x00,0x40,
  0x8d,0x14,0x20,0x00,0xa1,0xc8,0xda,0x02,0x81,0x3f,0xd9,0xf1,0xdb,0x40,0xa4,0xa6,0xaf};
 if(board_i2c_write(ADDR_OLED,init,sizeof init)) return true;
 board_gpio_write(PIN_DISPLAY_ISOLATE,true); (void)pmic_wr(0x1d,0x6c); return false;
}
bool oled_frame(const uint8_t frame[512]) {
 const uint8_t window[]={0,0x21,0,127,0x22,0,3};
 if(!board_i2c_write(ADDR_OLED,window,sizeof window)) return false;
 uint8_t packet[17];packet[0]=0x40;
 for(size_t i=0;i<512;i+=16){memcpy(packet+1,frame+i,16);if(!board_i2c_write(ADDR_OLED,packet,17))return false;}
 return true;
}
/* Small original seven-segment renderer; no font/library license dependency. */
bool oled_steps(uint64_t total) {
 static const uint8_t segments[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
 static const uint8_t bars[7][4]={{2,0,10,2},{12,2,2,10},{12,14,2,10},{2,24,10,2},
  {0,14,2,10},{0,2,2,10},{2,12,10,2}};
 uint8_t frame[512]={0}; unsigned digits[6]; total%=1000000;
 for(int i=5;i>=0;--i){digits[i]=(unsigned)(total%10);total/=10;}
 for(unsigned d=0;d<6;++d)for(unsigned seg=0;seg<7;++seg)if(segments[digits[d]]&(1u<<seg)){
  for(unsigned y=0;y<bars[seg][3];++y)for(unsigned x=0;x<bars[seg][2];++x){
   unsigned px=7+d*19+bars[seg][0]+x,py=3+bars[seg][1]+y;frame[(py/8)*128+px]|=(uint8_t)(1u<<(py%8));
  }
 }
 return oled_frame(frame);
}
static BMA400_INTF_RET_TYPE sensor_rd(uint8_t reg,uint8_t *b,uint32_t n,void *ctx){
 (void)ctx;return board_i2c_read_reg(ADDR_BMA400,reg,b,n)?0:-1;
}
static BMA400_INTF_RET_TYPE sensor_wr(uint8_t reg,const uint8_t *b,uint32_t n,void *ctx){
 (void)ctx;uint8_t packet[65];if(n>64)return -1;packet[0]=reg;memcpy(packet+1,b,n);
 return board_i2c_write(ADDR_BMA400,packet,n+1)?0:-1;
}
static void sensor_delay(uint32_t period,void *ctx){(void)ctx;board_delay_us(period);}
bool sensor_init(struct bma400_dev *dev,bool wrist){
 memset(dev,0,sizeof *dev);dev->intf=BMA400_I2C_INTF;dev->read=sensor_rd;dev->write=sensor_wr;dev->delay_us=sensor_delay;
 if(bma400_init(dev)!=BMA400_OK || bma400_soft_reset(dev)!=BMA400_OK)return false;
 struct bma400_sensor_conf cfg[2]={{0}};cfg[0].type=BMA400_STEP_COUNTER_INT;cfg[1].type=BMA400_ACCEL;
 if(bma400_get_sensor_conf(cfg,2,dev)!=BMA400_OK)return false;
 /* Count continuously in normal mode; don't substitute the 25 Hz low-power mode. */
 cfg[0].param.step_cnt.int_chan=BMA400_UNMAP_INT_PIN;
 cfg[1].param.accel.odr=BMA400_ODR_100HZ;cfg[1].param.accel.range=BMA400_RANGE_2G;
 cfg[1].param.accel.osr=BMA400_ACCEL_OSR_SETTING_0;cfg[1].param.accel.data_src=BMA400_DATA_SRC_ACCEL_FILT_1;
 if(bma400_set_sensor_conf(cfg,2,dev)!=BMA400_OK)return false;
 if(!wrist){const uint8_t nonwrist[24]={1,50,120,230,135,0,132,108,156,117,100,126,170,12,12,74,160,0,0,12,60,240,1,0};
  /* Validate coefficients against the vendored Bosch API before changing wear position. */
  if(bma400_set_step_counter_param(nonwrist,dev)!=BMA400_OK)return false;}
 struct bma400_device_conf pins={0};pins.type=BMA400_INT_PIN_CONF;
 pins.param.int_conf.int_chan=BMA400_INT_CHANNEL_1;
 pins.param.int_conf.pin_conf=BMA400_INT_OPEN_DRIVE_ACTIVE_0;
 if(bma400_set_device_conf(&pins,1,dev)!=BMA400_OK)return false;
 struct bma400_int_enable enable={BMA400_STEP_COUNTER_INT_EN,BMA400_ENABLE};
 return bma400_set_power_mode(BMA400_MODE_NORMAL,dev)==BMA400_OK && bma400_enable_interrupt(&enable,1,dev)==BMA400_OK;
}
bool sensor_read(struct bma400_dev *dev,uint32_t *steps){uint8_t activity;
 return bma400_get_steps_counted(steps,&activity,dev)==BMA400_OK;}
bool steps_update(StepAccumulator *s,uint32_t raw,uint32_t maximum){
 raw&=0xffffffu;if(!s->baseline_valid){s->previous_raw=raw;s->baseline_valid=true;return true;}
 uint32_t delta=(raw-s->previous_raw)&0xffffffu;s->previous_raw=raw;
 if(delta>maximum)return false; /* Reset/discontinuity: preserve total, establish a new baseline. */
 s->total+=delta;return true;
}
static bool gauge_control(uint16_t cmd){uint8_t b[]={0,(uint8_t)cmd,(uint8_t)(cmd>>8)};
 bool ok=board_i2c_write(ADDR_BQ27427,b,sizeof b);board_delay_us(2000);return ok;}
static bool gauge_wait_cfg(bool on){
 for(unsigned i=0;i<100;++i){uint16_t f;if(!rd16(ADDR_BQ27427,6,&f))return false;
  if(((f&0x10)!=0)==on)return true;board_delay_us(20000);}return false;
}
static void be16(uint8_t *b,uint16_t value){b[0]=(uint8_t)(value>>8);b[1]=(uint8_t)value;}
static uint8_t checksum(const uint8_t b[32]){uint8_t s=0;for(unsigned i=0;i<32;++i)s=(uint8_t)(s+b[i]);return (uint8_t)(255-s);}
bool gauge_configure_100mah(void){
 uint16_t id,flags;
 if(!gauge_control(1)||!rd16(ADDR_BQ27427,0,&id)||id!=0x0427||!rd16(ADDR_BQ27427,6,&flags))return false;
 if(!(flags&0x20))return true; /* Preserve learned RAM unless ITPOR is set. */
 /* Factory unseal key; an application with changed keys must supply those keys. */
 if(!gauge_control(0x8000)||!gauge_control(0x8000)||!gauge_control(0x0013)||!gauge_wait_cfg(true))return false;
 board_delay_us(1000000);
 bool ok=gauge_control(0x0030); /* CHEM_A, 4.2 V. Validate chemistry with actual cell. */
 uint8_t block[32],oldcs;
 ok=ok&&wr(ADDR_BQ27427,0x61,0)&&wr(ADDR_BQ27427,0x3e,82)&&wr(ADDR_BQ27427,0x3f,0);
 board_delay_us(10000);
 ok=ok&&board_i2c_read_reg(ADDR_BQ27427,0x40,block,32)&&board_i2c_read_reg(ADDR_BQ27427,0x60,&oldcs,1);
 if(ok)ok=checksum(block)==oldcs;
 if(ok){be16(block+6,100);be16(block+8,370);be16(block+10,3000);be16(block+21,500);be16(block+23,10);
  uint8_t packet[33];packet[0]=0x40;memcpy(packet+1,block,32);
  ok=board_i2c_write(ADDR_BQ27427,packet,33)&&wr(ADDR_BQ27427,0x60,checksum(block));
  board_delay_us(10000);uint8_t verify[32];
  ok=ok&&board_i2c_read_reg(ADDR_BQ27427,0x40,verify,32)&&memcmp(verify,block,32)==0;
 }
 /* Always attempt to leave config-update mode after an error. */
 bool exited=gauge_control(0x0042)&&gauge_wait_cfg(false);board_delay_us(2000000);
 return ok&&exited; /* Development stays unsealed; production may seal after verification. */
}
bool gauge_read(uint16_t *mv,uint16_t *soc){return rd16(ADDR_BQ27427,4,mv)&&rd16(ADDR_BQ27427,0x1c,soc)&&*soc<=100;}
void encode_snapshot(uint8_t out[14],uint64_t total,uint16_t mv,uint8_t soc,uint8_t flags){
 out[0]=1;out[1]=flags;for(unsigned i=0;i<8;++i)out[i+2]=(uint8_t)(total>>(i*8));
 out[10]=(uint8_t)mv;out[11]=(uint8_t)(mv>>8);out[12]=soc;out[13]=0;
}