Beispiel #1
0
bool i2c_read(void *i2c, uint8_t device_addr,
              uint8_t reg_addr, uint8_t len, uint8_t *buffer)
{
    Twihs *twi = (Twihs *)i2c;
    if (twi != TWIHS0) // sanity-check... todo: other instances
        return false;
    /*
    printf("starting %d-byte i2c read from device 0x%02x register 0x%02x\r\n",
        (int)len, (unsigned)device_addr, (unsigned)reg_addr);
    */
    twi->TWIHS_MMR =
        TWIHS_MMR_IADRSZ(1) |
        TWIHS_MMR_MREAD     |
        TWIHS_MMR_DADR(device_addr);
    twi->TWIHS_IADR = reg_addr;
    twi->TWIHS_CR = TWIHS_CR_START;
    for (int i = 0; i < len; i++)
    {
        //printf("waiting for byte %d to arrive...\r\n", i);
        while (!(twi->TWIHS_SR & TWIHS_SR_RXRDY));
        buffer[i] = twi->TWIHS_RHR;
        //printf("done! got byte %d\r\n", i);
        if (i == len - 2)
            twi->TWIHS_CR = TWIHS_CR_STOP;
    }
    while (!(twi->TWIHS_SR & TWIHS_SR_TXCOMP));
    //printf("done with twi read\r\n");
    return true;
}
Beispiel #2
0
/**
 * \brief Write multiple bytes to a TWIHS compatible slave device.
 *
 * \note This function will NOT return until all data has been written or error occurred.
 *
 * \param p_twihs Pointer to a TWIHS instance.
 * \param p_packet Packet information and data (see \ref twihs_packet_t).
 *
 * \return TWIHS_SUCCESS if all bytes were written, error code otherwise.
 */
uint32_t twihs_master_write(Twihs *p_twihs, twihs_packet_t *p_packet)
{
	uint32_t status, cnt = p_packet->length;
	uint8_t *buffer = p_packet->buffer;

	/* Check argument */
	if (cnt == 0) {
		return TWIHS_INVALID_ARGUMENT;
	}

	/* Set write mode, slave address and 3 internal address byte lengths */
	p_twihs->TWIHS_MMR = 0;
	p_twihs->TWIHS_MMR = TWIHS_MMR_DADR(p_packet->chip) |
			((p_packet->addr_length << TWIHS_MMR_IADRSZ_Pos) &
			TWIHS_MMR_IADRSZ_Msk);

	/* Set internal address for remote chip */
	p_twihs->TWIHS_IADR = 0;
	p_twihs->TWIHS_IADR = twihs_mk_addr(p_packet->addr, p_packet->addr_length);

	/* Send all bytes */
	while (cnt > 0) {
		status = p_twihs->TWIHS_SR;
		if (status & TWIHS_SR_NACK) {
			return TWIHS_RECEIVE_NACK;
		}

		if (!(status & TWIHS_SR_TXRDY)) {
			continue;
		}
		p_twihs->TWIHS_THR = *buffer++;

		cnt--;
	}

	while (1) {
		status = p_twihs->TWIHS_SR;
		if (status & TWIHS_SR_NACK) {
			return TWIHS_RECEIVE_NACK;
		}

		if (status & TWIHS_SR_TXRDY) {
			break;
		}
	}

	p_twihs->TWIHS_CR = TWIHS_CR_STOP;

	while (!(p_twihs->TWIHS_SR & TWIHS_SR_TXCOMP)) {
	}

	return TWIHS_SUCCESS;
}
Beispiel #3
0
/**
 * \brief Read multiple bytes from a TWIHS compatible slave device.
 *
 * \note This function will NOT return until all data has been read or error occurs.
 *
 * \param p_twihs Pointer to a TWIHS instance.
 * \param p_packet Packet information and data (see \ref twihs_packet_t).
 *
 * \return TWIHS_SUCCESS if all bytes were read, error code otherwise.
 */
uint32_t twihs_master_read(Twihs *p_twihs, twihs_packet_t *p_packet)
{
	uint32_t status, cnt = p_packet->length;
	uint8_t *buffer = p_packet->buffer;
	uint32_t timeout = TWIHS_TIMEOUT;

	/* Check argument */
	if (cnt == 0) {
		return TWIHS_INVALID_ARGUMENT;
	}

	/* Set read mode, slave address and 3 internal address byte lengths */
	p_twihs->TWIHS_MMR = 0;
	p_twihs->TWIHS_MMR = TWIHS_MMR_MREAD | TWIHS_MMR_DADR(p_packet->chip) |
			((p_packet->addr_length << TWIHS_MMR_IADRSZ_Pos) &
			TWIHS_MMR_IADRSZ_Msk);

	/* Set internal address for remote chip */
	p_twihs->TWIHS_IADR = 0;
	p_twihs->TWIHS_IADR = twihs_mk_addr(p_packet->addr, p_packet->addr_length);

	/* Send a START Condition */
	p_twihs->TWIHS_CR = TWIHS_CR_START;

	while (cnt > 0) {
		status = p_twihs->TWIHS_SR;
		if (status & TWIHS_SR_NACK) {
			return TWIHS_RECEIVE_NACK;
		}
		if (!timeout--) {
			return TWIHS_ERROR_TIMEOUT;
		}
		/* Last byte ? */
		if (cnt == 1) {
			p_twihs->TWIHS_CR = TWIHS_CR_STOP;
		}

		if (!(status & TWIHS_SR_RXRDY)) {
			continue;
		}
		*buffer++ = p_twihs->TWIHS_RHR;

		cnt--;
		timeout = TWIHS_TIMEOUT;
	}

	while (!(p_twihs->TWIHS_SR & TWIHS_SR_TXCOMP)) {
	}

	p_twihs->TWIHS_SR;

	return TWIHS_SUCCESS;
}
Beispiel #4
0
bool i2c_write(void *i2c, uint8_t device_addr,
               uint8_t reg_addr, uint8_t len, uint8_t *buffer)
{
    Twihs *twi = (Twihs *)i2c;
    if (twi != TWIHS0) // sanity-check... todo: other instances
        return false;
    twi->TWIHS_MMR =
        TWIHS_MMR_IADRSZ(1) |
        TWIHS_MMR_DADR(device_addr);
    twi->TWIHS_IADR = reg_addr;
    //twi->TWIHS_CR = TWIHS_CR_START;
    for (int i = 0; i < len; i++)
    {
        twi->TWIHS_THR = buffer[i];
        while (!(twi->TWIHS_SR & TWIHS_SR_TXRDY));
    }
    twi->TWIHS_CR = TWIHS_CR_STOP;
    while (!(twi->TWIHS_SR & TWIHS_SR_TXCOMP));
    return true;
}
/**
 * \ingroup freertos_twihs_peripheral_control_group
 * \brief Initiate a completely asynchronous multi-byte read operation on an TWIHS
 * peripheral.
 *
 * freertos_twihs_read_packet_async() is an ASF specific FreeRTOS driver function.
 * It configures the TWIHS peripheral DMA controller (PDC) to read data from the
 * TWIHS port, then returns.  freertos_twihs_read_packet_async() does not wait for
 * the reception to complete before returning.
 *
 * The FreeRTOS ASF TWIHS driver is initialized using a call to
 * freertos_twihs_master_init().  The freertos_driver_parameters.options_flags
 * parameter passed into the initialization function defines the driver behavior.
 * freertos_twihs_read_packet_async() can only be used if the
 * freertos_driver_parameters.options_flags parameter passed to the initialization
 * function had the WAIT_RX_COMPLETE bit clear.
 *
 * freertos_twihs_read_packet_async() is an advanced function and readers are
 * recommended to also reference the application note and examples that
 * accompany the FreeRTOS ASF drivers.  freertos_twihs_read_packet() is a version
 * that does not exit until the PDC transfer is complete, but still allows other
 * RTOS tasks to execute while the transmission is in progress.
 *
 * The FreeRTOS ASF driver both installs and handles the TWIHS PDC interrupts.
 * Users do not need to concern themselves with interrupt handling, and must
 * not install their own interrupt handler.
 *
 * \param p_twihs    The handle to the TWIHS port returned by the
 *     freertos_twihs_master_init() call used to initialise the port.
 * \param p_packet    Structure that defines the TWIHS transfer parameters, such
 *     as the I2C chip being addressed, the destination for the data being read,
 *     and the number of bytes to read.  twihs_packet_t is a standard ASF type (it
 *     is not FreeRTOS specific).
 * \param block_time_ticks    The FreeRTOS ASF TWIHS driver is initialized using a
 *     call to freertos_twihs_master_init().  The
 *     freertos_driver_parameters.options_flags parameter passed to the
 *     initialization function defines the driver behavior.  If
 *     freertos_driver_parameters.options_flags had the USE_RX_ACCESS_MUTEX bit
 *     set, then the driver will only read from the TWIHS peripheral if it has
 *     first gained exclusive access to it.  block_time_ticks specifies the
 *     maximum amount of time the driver will wait to get exclusive access
 *     before aborting the read operation.  Other tasks will execute during any
 *     waiting time.  block_time_ticks is specified in RTOS tick periods.  To
 *     specify a block time in milliseconds, divide the milliseconds value by
 *     portTICK_RATE_MS, and pass the result in block_time_ticks.
 *     portTICK_RATE_MS is defined by FreeRTOS.
 * \param notification_semaphore    The RTOS task that calls the receive
 *     function exits the receive function as soon as the reception starts.
 *     The data being received by the PDC cannot normally be processed until
 *     after the reception has completed.  The PDC interrupt (handled internally
 *     by the FreeRTOS ASF driver) 'gives' the semaphore when the PDC transfer
 *     completes.  The notification_semaphore therefore provides a mechanism for
 *     the calling task to know when the PDC has read the requested number of
 *     bytes.  The calling task can call standard FreeRTOS functions to block on
 *     the semaphore until the PDC interrupt occurs.  Other RTOS tasks will
 *     execute while the the calling task is in the Blocked state.  The
 *     semaphore must be created using the FreeRTOS vSemaphoreCreateBinary() API
 *     function before it is used as a parameter.
 *
 * \return     ERR_INVALID_ARG is returned if an input parameter is invalid.
 *     ERR_TIMEOUT is returned if block_time_ticks passed before exclusive
 *     access to the TWIHS peripheral could be obtained.  STATUS_OK is returned if
 *     the PDC was successfully configured to perform the TWIHS read operation.
 */
status_code_t freertos_twihs_read_packet_async(freertos_twihs_if p_twihs,
		twihs_packet_t *p_packet, portTickType block_time_ticks,
		xSemaphoreHandle notification_semaphore)
{
	status_code_t return_value;
	portBASE_TYPE twihs_index;
	Twihs *twihs_base;
	uint32_t internal_address = 0;

	twihs_base = (Twihs *) p_twihs;
	twihs_index = get_pdc_peripheral_details(all_twihs_definitions, MAX_TWIHS,
			(void *) twihs_base);

	/* Don't do anything unless a valid TWIHS pointer was used. */
	if ((twihs_index < MAX_TWIHS) && (p_packet->length > 0)) {
		/* Because the peripheral is half duplex, there is only one access mutex
		and the rx uses the tx mutex. */
		return_value = freertos_obtain_peripheral_access_mutex(
				&(tx_dma_control[twihs_index]), &block_time_ticks);

		if (return_value == STATUS_OK) {
			/* Ensure Rx is already empty. */
			twihs_read_byte(twihs_base);

			/* Set read mode and slave address. */
			twihs_base->TWIHS_MMR = 0;
			twihs_base->TWIHS_MMR = TWIHS_MMR_MREAD | TWIHS_MMR_DADR(
					p_packet->chip) |
					((p_packet->addr_length <<
					TWIHS_MMR_IADRSZ_Pos) &
					TWIHS_MMR_IADRSZ_Msk);

			/* Set internal address if any. */
			if (p_packet->addr_length) {
				internal_address = p_packet->addr [0];
				if (p_packet->addr_length > 1) {
					internal_address <<= 8;
					internal_address |= p_packet->addr[1];
				}

				if (p_packet->addr_length > 2) {
					internal_address <<= 8;
					internal_address |= p_packet->addr[2];
				}
			}
			twihs_base->TWIHS_IADR = internal_address;

			if (p_packet->length <= 2) {
				/* Do not handle errors for short packets in interrupt handler */
				twihs_disable_interrupt(
						all_twihs_definitions[twihs_index].peripheral_base_address,
						IER_ERROR_INTERRUPTS);

				/* Cannot use PDC transfer, use normal transfer */
				uint8_t stop_sent = 0;
				uint32_t cnt = p_packet->length;
				uint32_t status;
				uint8_t *buffer = p_packet->buffer;
				uint32_t timeout_counter = 0;

				/* Start the transfer. */
				if (cnt == 1) {
					twihs_base->TWIHS_CR = TWIHS_CR_START | TWIHS_CR_STOP;
					stop_sent = 1;
				} else {
					twihs_base->TWIHS_CR = TWIHS_CR_START;
				}

				while (cnt > 0) {
					status = twihs_base->TWIHS_SR;
					if (status & TWIHS_SR_NACK) {
						/* Re-enable interrupts */
						twihs_enable_interrupt(
								all_twihs_definitions[twihs_index].peripheral_base_address,
								IER_ERROR_INTERRUPTS);
						/* Release semaphore */
						xSemaphoreGive(tx_dma_control[twihs_index].peripheral_access_mutex);
						return TWIHS_RECEIVE_NACK;
					}
					/* Last byte ? */
					if (cnt == 1 && !stop_sent) {
						twihs_base->TWIHS_CR = TWIHS_CR_STOP;
						stop_sent = 1;
					}
					if (!(status & TWIHS_SR_RXRDY)) {
						if (++timeout_counter >= TWIHS_TIMEOUT_COUNTER) {
							return_value = ERR_TIMEOUT;
							break;
						}
						continue;
					}
					*buffer++ = twihs_base->TWIHS_RHR;
					cnt--;
					timeout_counter = 0;
				}

				timeout_counter = 0;
				/* Wait for stop to be sent */
				while (!(twihs_base->TWIHS_SR & TWIHS_SR_TXCOMP)) {
					/* Check timeout condition. */
					if (++timeout_counter >= TWIHS_TIMEOUT_COUNTER) {
						return_value = ERR_TIMEOUT;
						break;
					}
				}
				/* Re-enable interrupts */
				twihs_enable_interrupt(
						all_twihs_definitions[twihs_index].peripheral_base_address,
						IER_ERROR_INTERRUPTS);
				/* Release semaphores */
				xSemaphoreGive(tx_dma_control[twihs_index].peripheral_access_mutex);
			} else {
				/* Start the PDC reception. */
				twihss[twihs_index].buffer = p_packet->buffer;
				twihss[twihs_index].length = p_packet->length;
				freertos_start_pdc_rx(&(rx_dma_control[twihs_index]),
						p_packet->buffer, p_packet->length,
						all_twihs_definitions[twihs_index].pdc_base_address,
						notification_semaphore);

				/* Start the transfer. */
				twihs_base->TWIHS_CR = TWIHS_CR_START;

				/* Catch the end of reception so the access mutex can be returned,
				and the task notified (if it supplied a notification semaphore).
				The interrupt can be enabled here because the ENDRX	signal from the
				PDC to the peripheral will have been de-asserted when the next
				transfer was configured. */
				twihs_enable_interrupt(twihs_base, TWIHS_IER_ENDRX);

				return_value = freertos_optionally_wait_transfer_completion(
						&(rx_dma_control[twihs_index]),
						notification_semaphore,
						block_time_ticks);
			}
		}
	} else {
		return_value = ERR_INVALID_ARG;
	}

	return return_value;
}
/**
 * \ingroup freertos_twihs_peripheral_control_group
 * \brief Initiate a completely asynchronous multi-byte write operation on a TWIHS
 * peripheral.
 *
 * freertos_twihs_write_packet_async() is an ASF specific FreeRTOS driver function.
 * It configures the TWIHS peripheral DMA controller (PDC) to transmit data on the
 * TWIHS port, then returns.  freertos_twihs_write_packet_async() does not wait for
 * the transmission to complete before returning.
 *
 * The FreeRTOS TWIHS driver is initialized using a call to
 * freertos_twihs_master_init().  The freertos_driver_parameters.options_flags
 * parameter passed into the initialization function defines the driver behavior.
 * freertos_twihs_write_packet_async() can only be used if the
 * freertos_driver_parameters.options_flags parameter passed to the initialization
 * function had the WAIT_TX_COMPLETE bit clear.
 *
 * freertos_twihs_write_packet_async() is an advanced function and readers are
 * recommended to also reference the application note and examples that
 * accompany the FreeRTOS ASF drivers.  freertos_twihs_write_packet() is a version
 * that does not exit until the PDC transfer is complete, but still allows other
 * RTOS tasks to execute while the transmission is in progress.
 *
 * The FreeRTOS ASF driver both installs and handles the TWIHS PDC interrupts.
 * Users do not need to concern themselves with interrupt handling, and must
 * not install their own interrupt handler.
 *
 * \param p_twihs    The handle to the TWIHS peripheral returned by the
 *     freertos_twihs_master_init() call used to initialise the peripheral.
 * \param p_packet    Structure that defines the TWIHS transfer parameters, such
 *     as the I2C chip being addressed, the source data location, and the number
 *     of bytes to transmit.  twihs_packet_t is a standard ASF type (it is not
 *     FreeRTOS specific).
 * \param block_time_ticks    The FreeRTOS ASF TWIHS driver is initialized using a
 *     call to freertos_twihs_master_init().  The
 *     freertos_driver_parameters.options_flags parameter passed to the
 *     initialization function defines the driver behavior.  If
 *     freertos_driver_parameters.options_flags had the USE_TX_ACCESS_MUTEX bit
 *     set, then the driver will only write to the TWIHS peripheral if it has
 *     first gained exclusive access to it.  block_time_ticks specifies the
 *     maximum amount of time the driver will wait to get exclusive access
 *     before aborting the write operation.  Other tasks will execute during any
 *     waiting time.  block_time_ticks is specified in RTOS tick periods.  To
 *     specify a block time in milliseconds, divide the milliseconds value by
 *     portTICK_RATE_MS, and pass the result in block_time_ticks.
 *     portTICK_RATE_MS is defined by FreeRTOS.
 * \param notification_semaphore    The RTOS task that calls the transmit
 *     function exits the transmit function as soon as the transmission starts.
 *     The data being transmitted by the PDC must not be modified until after
 *     the transmission has completed.  The PDC interrupt (handled internally by
 *     the FreeRTOS ASF driver) 'gives' the semaphore when the PDC transfer
 *     completes.  The notification_semaphore therefore provides a mechanism for
 *     the calling task to know when the PDC has finished accessing the data.
 *     The calling task can call standard FreeRTOS functions to block on the
 *     semaphore until the PDC interrupt occurs.  Other RTOS tasks will execute
 *     while the the calling task is in the Blocked state.  The semaphore must
 *     be created using the FreeRTOS vSemaphoreCreateBinary() API function
 *     before it is used as a parameter.
 *
 * \return     ERR_INVALID_ARG is returned if an input parameter is invalid.
 *     ERR_TIMEOUT is returned if block_time_ticks passed before exclusive
 *     access to the TWIHS peripheral could be obtained.  STATUS_OK is returned if
 *     the PDC was successfully configured to perform the TWIHS write operation.
 */
status_code_t freertos_twihs_write_packet_async(freertos_twihs_if p_twihs,
		twihs_packet_t *p_packet, portTickType block_time_ticks,
		xSemaphoreHandle notification_semaphore)
{
	status_code_t return_value;
	portBASE_TYPE twihs_index;
	Twihs *twihs_base;
	uint32_t internal_address = 0;

	twihs_base = (Twihs *) p_twihs;
	twihs_index = get_pdc_peripheral_details(all_twihs_definitions, MAX_TWIHS,
			(void *) twihs_base);

	/* Don't do anything unless a valid TWIHS pointer was used. */
	if ((twihs_index < MAX_TWIHS) && (p_packet->length > 0)) {
		return_value = freertos_obtain_peripheral_access_mutex(
				&(tx_dma_control[twihs_index]), &block_time_ticks);

		if (return_value == STATUS_OK) {
			/* Set write mode and slave address. */
			twihs_base->TWIHS_MMR = 0;
			twihs_base->TWIHS_MMR = TWIHS_MMR_DADR(p_packet->chip) |
					((p_packet->addr_length <<
					TWIHS_MMR_IADRSZ_Pos) &
					TWIHS_MMR_IADRSZ_Msk);

			/* Set internal address if any. */
			if (p_packet->addr_length > 0) {
				internal_address = p_packet->addr[0];
				if (p_packet->addr_length > 1) {
					internal_address <<= 8;
					internal_address |= p_packet->addr[1];
				}

				if (p_packet->addr_length > 2) {
					internal_address <<= 8;
					internal_address |= p_packet->addr[2];
				}
			}
			twihs_base->TWIHS_IADR = internal_address;

			if (p_packet->length == 1) {
				uint32_t status;
				uint32_t timeout_counter = 0;
				/* Do not handle errors for short packets in interrupt handler */
				twihs_disable_interrupt(
						all_twihs_definitions[twihs_index].peripheral_base_address,
						IER_ERROR_INTERRUPTS);
				/* Send start condition */
				twihs_base->TWIHS_THR = *((uint8_t*)(p_packet->buffer));
				while (1) {
					status = twihs_base->TWIHS_SR;
					if (status & TWIHS_SR_NACK) {
						/* Re-enable interrupts */
						twihs_enable_interrupt(
								all_twihs_definitions[twihs_index].peripheral_base_address,
								IER_ERROR_INTERRUPTS);
						/* Release semaphore */
						xSemaphoreGive(tx_dma_control[twihs_index].peripheral_access_mutex);
						return TWIHS_RECEIVE_NACK;
					}
					if (status & TWIHS_SR_TXRDY) {
						break;
					}
					/* Check timeout condition. */
					if (++timeout_counter >= TWIHS_TIMEOUT_COUNTER) {
						return_value = ERR_TIMEOUT;
						break;
					}
				}
				twihs_base->TWIHS_CR = TWIHS_CR_STOP;
				/* Wait for TX complete */
				while (!(twihs_base->TWIHS_SR & TWIHS_SR_TXCOMP)) {
					/* Check timeout condition. */
					if (++timeout_counter >= TWIHS_TIMEOUT_COUNTER) {
						return_value = ERR_TIMEOUT;
						break;
					}
				}

				/* Re-enable interrupts */
				twihs_enable_interrupt(
						all_twihs_definitions[twihs_index].peripheral_base_address,
						IER_ERROR_INTERRUPTS);
				/* Release semaphores */
				xSemaphoreGive(tx_dma_control[twihs_index].peripheral_access_mutex);
			} else {

				twihss[twihs_index].buffer = p_packet->buffer;
				twihss[twihs_index].length = p_packet->length;

				freertos_start_pdc_tx(&(tx_dma_control[twihs_index]),
						p_packet->buffer, p_packet->length,
						all_twihs_definitions[twihs_index].pdc_base_address,
						notification_semaphore);

				/* Catch the end of transmission so the access mutex can be
				returned, and the task notified (if it supplied a notification
				semaphore).  The interrupt can be enabled here because the ENDTX
				signal from the PDC to the peripheral will have been de-asserted when
				the next transfer was configured. */
				twihs_enable_interrupt(twihs_base, TWIHS_IER_ENDTX);

				return_value = freertos_optionally_wait_transfer_completion(
						&(tx_dma_control[twihs_index]),
						notification_semaphore,
						block_time_ticks);
			}
		}
	} else {
		return_value = ERR_INVALID_ARG;
	}

	return return_value;
}