stm32f1/application.c

613 lines
27 KiB
C

/* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/** STM32F1 Maxim DS2432 (1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine) implementation
* @file application.c
* @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016-2017
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdlib.h> // standard utilities
#include <string.h> // string utilities
#include <time.h> // date/time utilities
/* STM32 (including CM3) libraries */
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
#include <libopencm3/cm3/scb.h> // vector table definition
#include <libopencm3/cm3/nvic.h> // interrupt utilities
#include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/rcc.h> // real-time control clock library
#include <libopencm3/stm32/exti.h> // external interrupt utilities
#include <libopencm3/stm32/rtc.h> // real time clock utilities
#include <libopencm3/stm32/iwdg.h> // independent watchdog utilities
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
#include <libopencm3/stm32/flash.h> // flash utilities
/* own libraries */
#include "global.h" // board definitions
#include "print.h" // printing utilities
#include "usart.h" // USART utilities
#include "usb_cdcacm.h" // USB CDC ACM utilities
#include "onewire_slave.h" // 1-Wire utilities
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
/** @defgroup main_flags flag set in interrupts to be processed in main task
* @{
*/
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
/** @} */
time_t time_rtc = 0; /**< time (seconds since Unix Epoch) */
struct tm* time_tm; /**< time in tm format (time zones are not handled for non-POSIX environments) */
size_t putc(char c)
{
size_t length = 0; // number of characters printed
static char newline = 0; // to remember on which character we sent the newline
if (0==c) {
length = 0; // don't print string termination character
} else if ('\r' == c || '\n' == c) { // send CR+LF newline for most carriage return and line feed combination
if (0==newline || c==newline) { // send newline only if not already send (and only once on \r\n or \n\r)
usart_putchar_nonblocking('\r'); // send CR over USART
usb_cdcacm_putchar('\r'); // send CR over USB
usart_putchar_nonblocking('\n'); // send LF over USART
usb_cdcacm_putchar('\n'); // send LF over USB
length += 2; // remember we printed 2 characters
newline = c; // remember on which character we sent the newline
} else {
length = 0; // the \r or \n of \n\r or \r\n has already been printed
}
} else {
usart_putchar_nonblocking(c); // send byte over USART
usb_cdcacm_putchar(c); // send byte over USB
newline = 0; // clear new line
length++; // remember we printed 1 character
}
return length; // return number of characters printed
}
/** user input command */
static char command[32] = {0};
/** user input command index */
uint8_t command_i = 0;
/** process user command
* @param[in] str user command string (\0 ended)
*/
static void process_command(char* str)
{
// split command
const char* delimiter = " ";
char* word = strtok(str,delimiter);
if (!word) {
goto error;
}
// parse command
if (0==strcmp(word,"h") || 0==strcmp(word,"help") || 0==strcmp(word,"?")) {
printf("available commands:\n");
printf("led [on|off|toggle]\n");
} else if (0==strcmp(word,"l") || 0==strcmp(word,"led")) {
word = strtok(NULL,delimiter);
if (!word) {
printf("LED is ");
if (gpio_get(GPIO(LED_PORT), GPIO(LED_PIN))) {
printf("on\n");
} else {
printf("off\n");
}
} else if (0==strcmp(word,"on")) {
led_on(); // switch LED on
printf("LED switched on\n"); // notify user
} else if (0==strcmp(word,"off")) {
led_off(); // switch LED off
printf("LED switched off\n"); // notify user
} else if (0==strcmp(word,"toggle")) {
led_toggle(); // toggle LED
printf("LED toggled\n"); // notify user
} else {
goto error;
}
} else if (0==strcmp(word,"time")) {
word = strtok(NULL,delimiter);
if (!word) {
time_rtc = rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time
printf("time: %02d:%02d:%02d\n", time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
} else if (strlen(word)!=8 || word[0]<'0' || word[0]>'2' || word[1]<'0' || word[1]>'9' || word[3]<'0' || word[3]>'5' || word[4]<'0' || word[4]>'9' || word[6]<'0' || word[6]>'5' || word[7]<'0' || word[7]>'9') { // time format is incorrect
goto error;
} else {
time_rtc = rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time
time_tm->tm_hour = (word[0]-'0')*10+(word[1]-'0')*1; // set hours
time_tm->tm_min = (word[3]-'0')*10+(word[4]-'0')*1; // set minutes
time_tm->tm_sec = (word[6]-'0')*10+(word[7]-'0')*1; // set seconds
time_rtc = mktime(time_tm); // get back seconds
rtc_set_counter_val(time_rtc); // save time to internal RTC
printf("time set\n");
}
} else if (0==strcmp(word,"date")) {
word = strtok(NULL,delimiter);
if (!word) {
time_rtc = rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time
printf("date: %d-%02d-%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon+1, time_tm->tm_mday);
} else if (strlen(word)!=10 || word[0]!='2' || word[1]!='0' || word[2]<'0' || word[2]>'9' || word[3]<'0' || word[3]>'9' || word[5]<'0' || word[5]>'1' || word[6]<'0' || word[6]>'9' || word[8]<'0' || word[8]>'3' || word[9]<'0' || word[9]>'9') {
goto error;
} else {
time_rtc = rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time
time_tm->tm_year = ((word[0]-'0')*1000+(word[1]-'0')*100+(word[2]-'0')*10+(word[3]-'0')*1)-1900; // set year
time_tm->tm_mon = (word[5]-'0')*10+(word[6]-'0')*1-1; // set month
time_tm->tm_mday = (word[8]-'0')*10+(word[9]-'0')*1; // set day
time_rtc = mktime(time_tm); // get back seconds
rtc_set_counter_val(time_rtc); // save time to internal RTC
printf("date set\n");
}
} else {
goto error;
}
return; // command successfully processed
error:
printf("command not recognized. enter help to list commands\n");
return;
}
/** static table used for the table_driven implementation
* Generated by pycrc v0.9, https://pycrc.org
* using the configuration:
* Width = 16
* Poly = 0x8005
* Xor_In = 0x0000
* ReflectIn = True
* Xor_Out = 0xffff
* ReflectOut = True
* Algorithm = table-driven
*/
static const uint16_t crc_table[256] = {
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
};
/** update the crc value with new data.
* @param crc The current crc value.
* @param data Pointer to a buffer of @a data_len bytes.
* @param data_len Number of bytes in the @a data buffer.
* @return The updated crc value.
*/
static uint16_t crc16_update(uint16_t crc, const uint8_t *data, size_t data_len)
{
const unsigned char *d = (const unsigned char *)data;
unsigned int tbl_idx;
while (data_len--) {
tbl_idx = (crc ^ *d) & 0xff;
crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffff;
d++;
}
return crc & 0xffff;
}
/** SHA-1 input data to calculate MAC for 'Read Authenticated Page' function command */
static uint32_t m[16] = {0};
/** intermediate calculation */
uint32_t wt[80] = {0};
/** DS2432 SHA-1 S function, rotating to the left
* @param[in] x value to rotate by @a n to the left
* @param[in] n rotate @a x by n to the left
* @return @a x rotated ny @a n to the left
*/
static uint32_t s_left(uint32_t x, uint8_t n) {
return (((x)<<(n)) | ((x)>>(32-(n))));
}
/** DS2432 SHA-1 F function
* @param[in] t time/round
* @param[in] b B value
* @param[in] c C value
* @param[in] d D value
* @return result of the F function
*/
static uint32_t f(uint8_t t, uint32_t b, uint32_t c, uint32_t d) {
if (t<20) {
return (b&c)|((~b)&d);
} else if (t<40) {
return b^c^d;
} else if (t<60) {
return (b&c)|(b&d)|(c&d);
} else if (t<80) {
return b^c^d;
} else { // this should not happen
return 0;
}
}
/** DS2432 SHA-1 K function
* @param[in] t time/round
* @return result of the K function
*/
static uint32_t k(uint8_t t) {
if (t<20) {
return 0x5A827999;
} else if (t<40) {
return 0x6ED9EBA1;
} else if (t<60) {
return 0x8F1BBCDC;
} else if (t<80) {
return 0xCA62C1D6;
} else { // this should not happen
return 0;
}
}
/** DS2432 SHA-1 W function
* @param[in] t time/round
* @note uses @a m or previously calculated @a wt values
* @return result of the W function
*/
static uint32_t w(uint8_t t) {
if (t<16) {
return m[t];
} else {
return s_left(wt[t-3]^wt[t-8]^wt[t-14]^wt[t-16], 1);
}
}
/** program entry point
* this is the firmware function started by the micro-controller
*/
void main(void);
void main(void)
{
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
#if DEBUG
// enable functionalities for easier debug
DBGMCU_CR |= DBGMCU_CR_IWDG_STOP; // stop independent watchdog counter when code is halted
DBGMCU_CR |= DBGMCU_CR_WWDG_STOP; // stop window watchdog counter when code is halted
DBGMCU_CR |= DBGMCU_CR_STANDBY; // allow debug also in standby mode (keep digital part and clock powered)
DBGMCU_CR |= DBGMCU_CR_STOP; // allow debug also in stop mode (keep clock powered)
DBGMCU_CR |= DBGMCU_CR_SLEEP; // allow debug also in sleep mode (keep clock powered)
#else
// setup watchdog to reset in case we get stuck (i.e. when an error occurred)
iwdg_set_period_ms(WATCHDOG_PERIOD); // set independent watchdog period
iwdg_start(); // start independent watchdog
#endif
board_setup(); // setup board
usart_setup(); // setup USART (for printing)
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
printf("welcome to the CuVoodoo STM32F1 DS2432 implementation (1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine)\n"); // print welcome message
#if !(DEBUG)
// show watchdog information
printf("watchdog set to (%.2fs)\n",WATCHDOG_PERIOD/1000.0);
if (FLASH_OBR&FLASH_OBR_OPTERR) {
printf("option bytes not set in flash: software wachtdog used (not started at reset)\n");
} else if (FLASH_OBR&FLASH_OBR_WDG_SW) {
printf("software wachtdog used (not started at reset)\n");
} else {
printf("hardware wachtdog used (started at reset)\n");
}
#endif
// setup RTC
printf("setup internal RTC: ");
rtc_auto_awake(RCC_LSE, 32768-1); // ensure internal RTC is on, uses the 32.678 kHz LSE, and the prescale is set to our tick speed, else update backup registers accordingly (power off the micro-controller for the change to take effect)
rtc_interrupt_enable(RTC_SEC); // enable RTC interrupt on "seconds"
nvic_enable_irq(NVIC_RTC_IRQ); // allow the RTC to interrupt
printf("OK\n");
time_rtc= rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time
printf("date: %d-%02d-%02d %02d:%02d:%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon+1, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
uint8_t ds2432_eeprom[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97}; /**< EEPROM content (including secret) */
uint8_t ds2432_scratchpad[8]; /**< scratchpad data */
uint8_t ds2432_address[2+1] = {0, 0, 0x5f}; /**< address registers: target address and E/S */
uint8_t ds2432_buffer[2+1+8+2]; /**< buffer to save command code target address, data status, scratchpad, and CRC */
const uint8_t ds2432_padding = 0xff; /**< padding byte used in Read Authenticated Page command */
uint16_t ds2432_crc = 0; // CRC-16 used in 1-Wire DS2432 communication
uint8_t ds2432_mac[20] = {0}; // buffer for the MAC
enum {
DS2432_STATE_IDLE,
DS2432_STATE_WRITE_SCRATCHPAD_DATA,
DS2432_STATE_WRITE_SCRATCHPAD_CRC,
DS2432_STATE_READ_SCRATCHPAD_ADDRESS,
DS2432_STATE_READ_SCRATCHPAD_DATA,
DS2432_STATE_READ_SCRATCHPAD_CRC,
DS2432_STATE_READ_AUTHENTICATED_PAGE_ADDRESS,
DS2432_STATE_READ_AUTHENTICATED_PAGE_DATA,
DS2432_STATE_READ_AUTHENTICATED_PAGE_PADDING,
DS2432_STATE_READ_AUTHENTICATED_PAGE_CRC,
DS2432_STATE_READ_AUTHENTICATED_PAGE_MAC,
DS2432_STATE_READ_AUTHENTICATED_PAGE_MACCRC,
DS2432_STATE_READ_MEMORY_ADDRESS,
DS2432_STATE_READ_MEMORY_DATA,
} ds2432_state = DS2432_STATE_IDLE; /**< current state */
printf("setup 1-Wire bus: ");
onewire_slave_setup(ds2432_eeprom[0x90], ((uint64_t)ds2432_eeprom[0x91]<<0)+((uint64_t)ds2432_eeprom[0x91]<<0)+((uint64_t)ds2432_eeprom[0x92]<<8)+((uint64_t)ds2432_eeprom[0x93]<<16)+((uint64_t)ds2432_eeprom[0x94]<<24)+((uint64_t)ds2432_eeprom[0x95]<<32)+((uint64_t)ds2432_eeprom[0x96]<<40)); // setup 1-Wire peripheral to act as slave
printf("OK\n");
// main loop
printf("command input: ready\n");
bool action = false; // if an action has been performed don't go to sleep
button_flag = false; // reset button flag
char ch = '\0'; // to store received character
bool char_flag = false; // a new character has been received
while (true) { // infinite loop
iwdg_reset(); // kick the dog
while (usart_received) { // data received over UART
action = true; // action has been performed
led_toggle(); // toggle LED
ch = usart_getchar(); // store receive character
char_flag = true; // notify character has been received
}
while (usb_cdcacm_received) { // data received over USB
action = true; // action has been performed
led_toggle(); // toggle LED
ch = usb_cdcacm_getchar(); // store receive character
char_flag = true; // notify character has been received
}
while (char_flag) { // user data received
char_flag = false; // reset flag
action = true; // action has been performed
printf("%c",ch); // echo receive character
if (ch=='\r' || ch=='\n') { // end of command received
if (command_i>0) { // there is a command to process
command[command_i] = 0; // end string
command_i = 0; // prepare for next command
process_command(command); // process user command
}
} else { // user command input
command[command_i] = ch; // save command input
if (command_i<LENGTH(command)-2) { // verify if there is place to save next character
command_i++; // save next character
}
}
}
while (button_flag) { // user pressed button
action = true; // action has been performed
led_toggle(); // toggle LED
printf("button pressed\n");
button_flag = false; // reset flag
}
while (rtc_internal_tick_flag) { // the internal RTC ticked
rtc_internal_tick_flag = false; // reset flag
action = true; // action has been performed
#if !defined(BLUE_PILL) // on the blue pill the LED is close to the 32.768 kHz oscillator and heavily influences it
//led_toggle(); // toggle LED (good to indicate if main function is stuck)
#endif
time_rtc = rtc_get_counter_val(); // get time from internal RTC (seconds since Unix Epoch)
time_tm = localtime(&time_rtc); // get time in tm format from Epoch (time zones are not handled for non-POSIX environments)
if (0==time_tm->tm_sec) { // new minute
printf("time: %02d:%02d:%02d\n", time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
}
}
while (onewire_slave_function_code_received) { // we received a function command code over the 1-Wire bus
onewire_slave_function_code_received = false; // reset flag
action = true; // action has been performed
switch (onewire_slave_function_code) {
case 0x0f: // Write Scratchpad
onewire_slave_function_read(ds2432_buffer, 2+8); // read target address and scratchpad
ds2432_state = DS2432_STATE_WRITE_SCRATCHPAD_DATA; // update state
break;
case 0xaa: // Read Scratchpad
onewire_slave_function_write(ds2432_address, LENGTH(ds2432_address)); // send address registers
ds2432_state = DS2432_STATE_READ_SCRATCHPAD_ADDRESS; // update state
break;
case 0xa5: // Read Authenticated Page
onewire_slave_function_read(ds2432_buffer, 2); // read target address
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_ADDRESS; // update state
break;
case 0xf0: // Read Memory
onewire_slave_function_read(ds2432_buffer, 2); // read target address
ds2432_state = DS2432_STATE_READ_MEMORY_ADDRESS; // update state
break;
default: // unknown function command code
ds2432_state = DS2432_STATE_IDLE; // return to idle state
break;
}
printf("1-Wire function command received: 0x%02x\n", onewire_slave_function_code);
}
while (onewire_slave_transfer_complete) { // the current data transfer completed
onewire_slave_transfer_complete = false; // reset flag
action = true; // action has been performed
switch (ds2432_state) {
case DS2432_STATE_WRITE_SCRATCHPAD_DATA:
ds2432_crc = crc16_update(0, (const uint8_t *)&onewire_slave_function_code, 1); // initialize CRC with function code
ds2432_crc = crc16_update(ds2432_crc, ds2432_buffer, (2+8)); // calculate CRC
ds2432_crc ^= 0xffff; // invert CRC
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // write CRC
ds2432_state = DS2432_STATE_WRITE_SCRATCHPAD_CRC; // update state
ds2432_address[0] = ds2432_buffer[0]&0xf8; // save target address
ds2432_address[1] = ds2432_buffer[1]; // save target address
ds2432_address[2] = 0x5f; // reset state
for (uint8_t i=0; i<LENGTH(ds2432_scratchpad) && i<LENGTH(ds2432_buffer)-2; i++) {
ds2432_scratchpad[i] = ds2432_buffer[i+2]; // save scratchpad
}
break;
case DS2432_STATE_READ_SCRATCHPAD_ADDRESS:
onewire_slave_function_write(ds2432_scratchpad, LENGTH(ds2432_scratchpad)); // send scratchpad
ds2432_state = DS2432_STATE_READ_SCRATCHPAD_DATA; // update state
ds2432_crc = crc16_update(0, (const uint8_t *)&onewire_slave_function_code, 1); // initialize CRC with function code
ds2432_crc = crc16_update(ds2432_crc, ds2432_address, LENGTH(ds2432_address)); // calculate CRC
ds2432_crc = crc16_update(ds2432_crc, ds2432_scratchpad, LENGTH(ds2432_scratchpad)); // calculate CRC
ds2432_crc ^= 0xffff; // invert CRC
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
break;
case DS2432_STATE_READ_SCRATCHPAD_DATA: // scratchpad data transfer complete, send CRC
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // send CRC
ds2432_state = DS2432_STATE_WRITE_SCRATCHPAD_CRC; // update state
break;
case DS2432_STATE_READ_AUTHENTICATED_PAGE_ADDRESS: // target address transfer completed, send data
if (0==ds2432_buffer[1] && ds2432_buffer[0]<0x80) { // target address matches to memory page
onewire_slave_function_write(&ds2432_eeprom[ds2432_buffer[0]], 0x20-(ds2432_buffer[0]&0x1f)); // send memory data until end of page
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_DATA; // update state
} else { // target address is out of range
ds2432_state = DS2432_STATE_IDLE; // return to idle state
}
break;
case DS2432_STATE_READ_AUTHENTICATED_PAGE_DATA: // memory page data transfer completed, send padding byte
onewire_slave_function_write((uint8_t *)&ds2432_padding, 1); // send padding byte
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_PADDING; // update state
ds2432_crc = crc16_update(0, (const uint8_t *)&onewire_slave_function_code, 1); // initialize CRC with function code
ds2432_crc = crc16_update(ds2432_crc, ds2432_buffer, 2); // update CRC with target address
ds2432_crc = crc16_update(ds2432_crc, &ds2432_eeprom[ds2432_buffer[0]], 0x20-(ds2432_buffer[0]&0x1f)); // update CRC with data
ds2432_crc = crc16_update(ds2432_crc, &ds2432_padding, 1); // update CRC with padding
ds2432_crc ^= 0xffff; // invert CRC
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
break;
case DS2432_STATE_READ_AUTHENTICATED_PAGE_PADDING: // padding byte transfer complete, send CRC
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // send CRC
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_CRC; // update state
// calculate MAC
{
ds2432_buffer[0] &= 0xe0; // set target address to start of page
// initialize SHA-1 input data (see table 4)
m[0] = (ds2432_eeprom[0x80+0]<<24)+(ds2432_eeprom[0x80+1]<<16)+(ds2432_eeprom[0x80+2]<<8)+(ds2432_eeprom[0x80+3]<<0); // copy secret
for (uint8_t i=0; i<8; i++) { // copy page
m[1+i] = (ds2432_eeprom[ds2432_buffer[0]+i*4+0]<<24)+(ds2432_eeprom[ds2432_buffer[0]+i*4+1]<<16)+(ds2432_eeprom[ds2432_buffer[0]+i*4+2]<<8)+(ds2432_eeprom[ds2432_buffer[0]+i*4+3]<<0);
}
m[9] = (0xff<<24)+(0xff<<16)+(0xff<<8)+(0xff<<0); // filling bytes
m[10] = ((0x40+(ds2432_buffer[0]>>5))<<24)+(ds2432_eeprom[0x90+0]<<16)+(ds2432_eeprom[0x90+1]<<8)+(ds2432_eeprom[0x90+2]<<0); // copy target and ROM code
m[11] = (ds2432_eeprom[0x90+3]<<24)+(ds2432_eeprom[0x90+4]<<16)+(ds2432_eeprom[0x90+5]<<8)+(ds2432_eeprom[0x90+6]<<0); // copy ROM code
m[12] = (ds2432_eeprom[0x80+4]<<24)+(ds2432_eeprom[0x80+5]<<16)+(ds2432_eeprom[0x80+6]<<8)+(ds2432_eeprom[0x80+7]<<0); // copy rest of secret
m[13] = (ds2432_scratchpad[4]<<24)+(ds2432_scratchpad[5]<<16)+(ds2432_scratchpad[6]<<8)+(0x80<<0); // copy challenge
m[14] = (0x00<<24)+(0x00<<16)+(0x00<<8)+(0x00<<0);
m[15] = (0x00<<24)+(0x00<<16)+(0x01<<8)+(0xb8<<0);
// initialize variables
uint32_t a = 0x67452301;
uint32_t b = 0xEFCDAB89;
uint32_t c = 0x98BADCFE;
uint32_t d = 0x10325476;
uint32_t e = 0xC3D2E1F0;
// loop through computation
for (uint8_t t=0; t<80; t++) {
wt[t] = w(t);
uint32_t tmp = s_left(a, 5)+f(t,b,c,d)+wt[t]+k(t)+e;
e = d;
d = c;
c = s_left(b, 30);
b = a;
a = tmp;
}
// copy result
ds2432_mac[0] = e>>0;
ds2432_mac[1] = e>>8;
ds2432_mac[2] = e>>16;
ds2432_mac[3] = e>>24;
ds2432_mac[4] = d>>0;
ds2432_mac[5] = d>>8;
ds2432_mac[6] = d>>16;
ds2432_mac[7] = d>>24;
ds2432_mac[8] = c>>0;
ds2432_mac[9] = c>>8;
ds2432_mac[10] = c>>16;
ds2432_mac[11] = c>>24;
ds2432_mac[12] = b>>0;
ds2432_mac[13] = b>>8;
ds2432_mac[14] = b>>16;
ds2432_mac[15] = b>>24;
ds2432_mac[16] = a>>0;
ds2432_mac[17] = a>>8;
ds2432_mac[18] = a>>16;
ds2432_mac[19] = a>>24;
}
break;
case DS2432_STATE_READ_AUTHENTICATED_PAGE_CRC: // CRC transfer completed, send MAC
onewire_slave_function_write(ds2432_mac, LENGTH(ds2432_mac)); // send CRC
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_MAC; // update state
ds2432_crc = crc16_update(0, ds2432_mac, LENGTH(ds2432_mac)); // calculate CRC for MAC
ds2432_crc ^= 0xffff; // invert CRC
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
break;
case DS2432_STATE_READ_AUTHENTICATED_PAGE_MAC: // padding byte transfer complete, send CRC
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // send CRC
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_MACCRC; // update state
break;
case DS2432_STATE_READ_MEMORY_ADDRESS:
if (0==ds2432_buffer[1] && ds2432_buffer[0]<0x97) { // target address is in EEPROM range
onewire_slave_function_write(&ds2432_eeprom[ds2432_buffer[0]], LENGTH(ds2432_eeprom)-ds2432_buffer[0]); // send memory data until end of page
ds2432_state = DS2432_STATE_READ_MEMORY_DATA; // update state
} else { // target address is out of range
ds2432_state = DS2432_STATE_IDLE; // return to idle state
}
break;
default: // unknown or end state
ds2432_state = DS2432_STATE_IDLE; // return to idle state
break;
}
printf("1-Wire transfer complete\n");
}
if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false;
} else {
__WFI(); // go to sleep
}
} // main loop
}
/** @brief interrupt service routine called when tick passed on RTC */
void rtc_isr(void)
{
rtc_clear_flag(RTC_SEC); // clear flag
rtc_internal_tick_flag = true; // notify to show new time
}