📜 ⬆️ ⬇️

1-Wire slave on MK. Part 2: Implementing in code


The first article of the cycle: 1-Wire slave on MK. Part 1: Iron

What is in the Internet on 1-Wire slave


On the Internet about the implementation of the 1-Wire wizard you can find a lot of information, articles, application notes for microcontrollers for every taste and color.
But for the implementation of the Slave materials a little. Source codes and even less. As a result, one source was found for PIC, with assembly inserts and errors. Recently there was an article on Habré for MSP430 from resetnow . Under the cut is our version of the task.

Little about the target controller


ATSAMD20G16 ( link to the online store) is an Atmel microcontroller with a Cortex-M0 + core. Its main characteristics are:

Periphery:

Implementation


Atmel Studio 6.2 + ASF was chosen for software development for MK.
To work correctly, you must implement the command:

An external interrupt was used to track the level change on the OW bus. To measure the time intervals - the timer (tracking signal reset) and the system frequency of the controller (for everything else).
Thus, the resources used MK: total clocking, timer, external interrupt, I / O ports.
Clocking MK 8 MHz, internal RC-generator.
')
The main algorithm is:

When creating a new project in Atmel Studio, it is possible to select an empty project for the corresponding debug board. This project already contains the necessary initialization of the clock frequency, I / O ports and other peripherals if necessary.
Therefore, consider the code fragments that are directly responsible for the logic of work.
External interrupt

For it to work, you must first initialize it, and then write an interrupt-handling procedure (callback).
The initialization of all modules in ASF occurs in one scenario: the default values ​​are read into the structure responsible for the module parameters. Then the necessary changes are made to these values, and they are set. Further, if necessary, those interrupts are recorded that will be used in this module (there may be more than one of them). Finally, the execution of interrupt handlers (callback) is allowed.

External interrupt initialization code:
void configure_ext_int(void) { //----------------------channel 2----------------------------------------- extint_chan_get_config_defaults(&eic_conf_2); eic_conf_2.gpio_pin = OW_IN_PIN_INT; eic_conf_2.gpio_pin_mux = OW_IN_PIN_MUX; eic_conf_2.gpio_pin_pull = EXTINT_PULL_NONE; eic_conf_2.detection_criteria = EXTINT_DETECT_RISING; eic_conf_2.filter_input_signal = true; extint_chan_set_config( OW_IN_INT_CHANNEL, &eic_conf_2); // Register and enable the callback function extint_register_callback(extint_user_callback_2, OW_IN_INT_CHANNEL,EXTINT_CALLBACK_TYPE_DETECT); extint_chan_enable_callback(OW_IN_INT_CHANNEL,EXTINT_CALLBACK_TYPE_DETECT); } 

From the code it can be seen that the difference from the default settings is to choose an I / O port, the interruption on which is monitored, the absence of a pull-up, and the event on which the interruption occurs (on the rising edge).

Timer

Timer initialization code:
 void configure_tc0(void) { struct tc_config config_tc; tc_get_config_defaults(&config_tc); config_tc.counter_size = TC_COUNTER_SIZE_16BIT; config_tc.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ; config_tc.counter_16_bit.compare_capture_channel[0] = 350; config_tc.clock_prescaler=TC_CLOCK_PRESCALER_DIV8; config_tc.clock_source=GCLK_GENERATOR_0; config_tc.reload_action=TC_RELOAD_ACTION_RESYNC; tc_init(&tc_instance_tc0, TC0, &config_tc); tc_enable(&tc_instance_tc0); tc_stop_counter(&tc_instance_tc0); } 

From the code it can be seen that the timer is switched to the 16-bit counter mode, the operation by frequency coincidence, count up to 350 ticks, the prescaler 8, clocked from the main clock signal (8 MHz).
Of particular interest is the delay function for a certain number of microseconds. It is necessary for "mutual understanding" between devices on the bus. Since the ASF library is sufficiently “heavy”, the generation of small time delays (on the order of several microseconds) required the use of an inline function, as well as optimization of O3.

Code:
 inline void my_delay_us(uint32_t usec) { volatile uint32_t ctr_reg_shdw ; ctr_reg_shdw = SysTick->CTRL; // Clear count flag by reading reg SysTick->LOAD = (usec) * (7880000 / 1000000); SysTick->VAL = 0; SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; // 0x5 // wait for flag, do not need interrupts here while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); } 


External interrupt handler code on the rising edge:
 void extint_user_callback_2(void) { if(reset_flag==0) { reset_flag=1; tc_set_count_value(&tc_instance_tc0, 0); tc_start_counter(&tc_instance_tc0); port_pin_set_output_level(LED_0_PIN, 0); } else { tc_set_count_value(&tc_instance_tc0, 0); reset_flag=0; } } 

Timer Interrupt Handler Code
 void tc0_callback(struct tc_module *const module_inst) { volatile unsigned char tmp=0; if(port_pin_get_input_level(PIN_PA04)!=0) { while((OW_check_in_level())!=0); if(reset_flag==1) { reset_flag=2; SendPresense(); tmp=0; tmp=get_byte(); if (tmp==0xf0) { search_rom(); mute_on=0; reset_flag=0; } else { // comeend "read UID" if(tmp==0x33) { //send UID for(unsigned char i=0;i<8;i++) send_byte(ownuid[i]); } // match rom if(tmp==0x55) { // wait for own UID+ command+address // potential problem: if less than 10 bytes received program hangs here for(unsigned char i=0;i<10;i++) in_buffer[i]=get_byte(); // if own UID if((in_buffer[0]==ownuid[0])&&(in_buffer[1]==ownuid[1])&&(in_buffer[2]==ownuid[2]) &&(in_buffer[3]==ownuid[3])&&(in_buffer[4]==ownuid[4])&&(in_buffer[5]==ownuid[5]) &&(in_buffer[6]==ownuid[6])&&(in_buffer[7]==ownuid[7])) { // if command 0xA0 (reading) if(in_buffer[8]==0xa0) { // my_delay_us(3); // what address to read from? if(in_buffer[9]==0) { // count crc registers[0]=0; registers[0] = OWI_ComputeCRC8(registers[1], registers[0]); // send value and crc send_byte(registers[1]); send_byte(registers[0]); } ….. } // if command 0xA1 (write) if(in_buffer[8]==0xa1) { write_flag=1; in_buffer[10]=get_byte(); //my_delay_us(3); // what address to write to if((in_buffer[9]==0)||(in_buffer[9]==1)||(in_buffer[9]==2)) { //nothing to do, as they are read only } if(in_buffer[9]==3) { registers[4]=in_buffer[10]; } …. } }// end of if my_uid }//end of if(tmp==0x55) } }//end of if(reset_flag==1) } //end of if((PORTD.IN&0x01)==0) else reset_flag=0; } 



The main difficulties in the implementation of the device were the selection and accuracy of timings, as well as the time of reading / setting the signal.
Timing problem was solved using inline + systick. The accuracy of the moment of reading / setting the signal is ensured by waiting for the level difference on the foot, which means the beginning of the time slot.

In the third part of the cycle, we will explain how to create your own device class for the OWFS library with its own list of parameters.

Source: https://habr.com/ru/post/275377/


All Articles