STM32 DMA: байты, оставшиеся в буфере, закодированы?
Долгое время я боролся с DMA-связью с двумя платами STM32 в той или иной форме. Моя текущая проблема заключается в следующем.
У меня есть хост (Raspberry Pi), выполняющий следующий код, ожидающий, пока плата инициализирует связь:
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
unsigned int usbdev;
unsigned char c;
system("stty -F /dev/ttyUSB0 921600 icanon raw");
usbdev = open("/dev/ttyUSB0", O_RDWR);
setbuf(stdout, NULL);
fprintf(stderr, "Waiting for signal..\n");
while (!read(usbdev, &c, 1));
unsigned char buf[] = "Defend at noon\r\n";
write(usbdev, buf, 16);
fprintf(stderr, "Written 16 bytes\r\n");
while (1) {
while (!read(usbdev, &c, 1));
printf("%c", c);
}
return 0;
}
По сути, он ожидает один байт данных, прежде чем отправит "Защитить в полдень" на доску, после чего печатает все, что отправлено обратно.
Доски сначала отправляют один байт, а затем ждут все входящие данные, заменяют несколько байтов и отправляют их обратно. Смотрите код в конце этого поста. Плата может быть либо STM32L100C, либо STM32F407 (на практике это платы обнаружения); На данный момент я испытываю одинаковое поведение с обоими.
Выходной я вижу (в хороший день - в плохой день зависает Written 16 bytes
) является следующим:
Waiting for signal..
Written 16 bytes
^JDefend adawnon
Как видите, данные отправляются и четыре байта заменяются, как и ожидалось, но впереди есть еще два символа (^J
, или же 0x5E
а также 0x4A
). Это оказывается прямым следствием signal_host
функция. Когда я заменяю символ на что-то произвольное (например, x
), это то, что выводится в этой позиции. Интересно отметить, что \n
на самом деле превращается в свою каретную нотацию ^J
где-то вдоль дороги. Похоже, что это происходит в связи с платой, потому что, когда я просто жестко закодирую строку в буфере и использую dma_transmit
чтобы отправить это в неинтерактивную хост-программу, она печатается просто отлично.
Похоже, я как-то неправильно настроил DMA в том смысле, что есть некоторый буфер, который не очищается должным образом. Кроме того, я не очень доверяю тому, как программа на стороне хоста использует stty. Однако в прошлом я действительно работал без сбоев, используя этот точный код. Я сравнил его с кодом, хранящимся в моей истории Git в течение нескольких месяцев, и я не могу найти разницу / недостаток.
Обратите внимание, что приведенный ниже код использует libopencm3 и основан на примерах из libopencm3-examples.
Код STM32L1:
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/dma.h>
void clock_setup(void)
{
rcc_clock_setup_pll(&clock_config[CLOCK_VRANGE1_HSI_PLL_32MHZ]);
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_USART2);
rcc_periph_clock_enable(RCC_DMA1);
}
void gpio_setup(void)
{
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3);
gpio_set_af(GPIOA, GPIO_AF7, GPIO2 | GPIO3);
}
void usart_setup(int baud)
{
usart_set_baudrate(USART2, baud);
usart_set_databits(USART2, 8);
usart_set_stopbits(USART2, USART_STOPBITS_1);
usart_set_mode(USART2, USART_MODE_TX_RX);
usart_set_parity(USART2, USART_PARITY_NONE);
usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
usart_enable(USART2);
}
void dma_request_setup(void)
{
dma_channel_reset(DMA1, DMA_CHANNEL6);
nvic_enable_irq(NVIC_DMA1_CHANNEL6_IRQ);
dma_set_peripheral_address(DMA1, DMA_CHANNEL6, (uint32_t) &USART2_DR);
dma_set_read_from_peripheral(DMA1, DMA_CHANNEL6);
dma_set_peripheral_size(DMA1, DMA_CHANNEL6, DMA_CCR_PSIZE_8BIT);
dma_set_memory_size(DMA1, DMA_CHANNEL6, DMA_CCR_MSIZE_8BIT);
dma_set_priority(DMA1, DMA_CHANNEL6, DMA_CCR_PL_VERY_HIGH);
dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL6);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL6);
dma_disable_transfer_error_interrupt(DMA1, DMA_CHANNEL6);
dma_disable_half_transfer_interrupt(DMA1, DMA_CHANNEL6);
dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL6);
}
void dma_transmit_setup(void)
{
dma_channel_reset(DMA1, DMA_CHANNEL7);
nvic_enable_irq(NVIC_DMA1_CHANNEL7_IRQ);
dma_set_peripheral_address(DMA1, DMA_CHANNEL7, (uint32_t) &USART2_DR);
dma_set_read_from_memory(DMA1, DMA_CHANNEL7);
dma_set_peripheral_size(DMA1, DMA_CHANNEL7, DMA_CCR_PSIZE_8BIT);
dma_set_memory_size(DMA1, DMA_CHANNEL7, DMA_CCR_MSIZE_8BIT);
dma_set_priority(DMA1, DMA_CHANNEL7, DMA_CCR_PL_VERY_HIGH);
dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL7);
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL7);
dma_disable_transfer_error_interrupt(DMA1, DMA_CHANNEL7);
dma_disable_half_transfer_interrupt(DMA1, DMA_CHANNEL7);
dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL7);
}
void dma_request(void* buffer, const int datasize)
{
dma_set_memory_address(DMA1, DMA_CHANNEL6, (uint32_t) buffer);
dma_set_number_of_data(DMA1, DMA_CHANNEL6, datasize);
dma_enable_channel(DMA1, DMA_CHANNEL6);
signal_host();
usart_enable_rx_dma(USART2);
}
void dma_transmit(const void* buffer, const int datasize)
{
dma_set_memory_address(DMA1, DMA_CHANNEL7, (uint32_t) buffer);
dma_set_number_of_data(DMA1, DMA_CHANNEL7, datasize);
dma_enable_channel(DMA1, DMA_CHANNEL7);
usart_enable_tx_dma(USART2);
}
int dma_done(void)
{
return !((DMA1_CCR6 | DMA1_CCR7) & 1);
}
void dma1_channel6_isr(void) {
usart_disable_rx_dma(USART2);
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL6, DMA_TCIF);
dma_disable_channel(DMA1, DMA_CHANNEL6);
}
void dma1_channel7_isr(void) {
usart_disable_tx_dma(USART2);
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL7, DMA_TCIF);
dma_disable_channel(DMA1, DMA_CHANNEL7);
}
void signal_host(void) {
usart_send_blocking(USART2, '\n');
}
int main(void)
{
clock_setup();
gpio_setup();
usart_setup(921600);
dma_transmit_setup();
dma_request_setup();
unsigned char buf[16];
dma_request(buf, 16); while (!dma_done());
buf[10] = 'd';
buf[11] = 'a';
buf[12] = 'w';
buf[13] = 'n';
dma_transmit(buf, 16); while (!dma_done());
while(1);
return 0;
}
Код STM32F4:
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/stm32/dma.h>
void clock_setup(void)
{
rcc_clock_setup_hse_3v3(&hse_8mhz_3v3[CLOCK_3V3_168MHZ]);
rcc_periph_clock_enable(RCC_GPIOA);
rcc_periph_clock_enable(RCC_USART2);
rcc_periph_clock_enable(RCC_DMA1);
}
void gpio_setup(void)
{
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3);
gpio_set_af(GPIOA, GPIO_AF7, GPIO2 | GPIO3);
}
void usart_setup(int baud)
{
usart_set_baudrate(USART2, baud);
usart_set_databits(USART2, 8);
usart_set_stopbits(USART2, USART_STOPBITS_1);
usart_set_mode(USART2, USART_MODE_TX_RX);
usart_set_parity(USART2, USART_PARITY_NONE);
usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
usart_enable(USART2);
}
void dma_request_setup(void)
{
dma_stream_reset(DMA1, DMA_STREAM5);
nvic_enable_irq(NVIC_DMA1_STREAM5_IRQ);
dma_set_peripheral_address(DMA1, DMA_STREAM5, (uint32_t) &USART2_DR);
dma_set_transfer_mode(DMA1, DMA_STREAM5, DMA_SxCR_DIR_PERIPHERAL_TO_MEM);
dma_set_peripheral_size(DMA1, DMA_STREAM5, DMA_SxCR_PSIZE_8BIT);
dma_set_memory_size(DMA1, DMA_STREAM5, DMA_SxCR_MSIZE_8BIT);
dma_set_priority(DMA1, DMA_STREAM5, DMA_SxCR_PL_VERY_HIGH);
dma_disable_peripheral_increment_mode(DMA1, DMA_SxCR_CHSEL_4);
dma_enable_memory_increment_mode(DMA1, DMA_STREAM5);
dma_disable_transfer_error_interrupt(DMA1, DMA_STREAM5);
dma_disable_half_transfer_interrupt(DMA1, DMA_STREAM5);
dma_disable_direct_mode_error_interrupt(DMA1, DMA_STREAM5);
dma_disable_fifo_error_interrupt(DMA1, DMA_STREAM5);
dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM5);
}
void dma_transmit_setup(void)
{
dma_stream_reset(DMA1, DMA_STREAM6);
nvic_enable_irq(NVIC_DMA1_STREAM6_IRQ);
dma_set_peripheral_address(DMA1, DMA_STREAM6, (uint32_t) &USART2_DR);
dma_set_transfer_mode(DMA1, DMA_STREAM6, DMA_SxCR_DIR_MEM_TO_PERIPHERAL);
dma_set_peripheral_size(DMA1, DMA_STREAM6, DMA_SxCR_PSIZE_8BIT);
dma_set_memory_size(DMA1, DMA_STREAM6, DMA_SxCR_MSIZE_8BIT);
dma_set_priority(DMA1, DMA_STREAM6, DMA_SxCR_PL_VERY_HIGH);
dma_disable_peripheral_increment_mode(DMA1, DMA_SxCR_CHSEL_4);
dma_enable_memory_increment_mode(DMA1, DMA_STREAM6);
dma_disable_transfer_error_interrupt(DMA1, DMA_STREAM6);
dma_disable_half_transfer_interrupt(DMA1, DMA_STREAM6);
dma_disable_direct_mode_error_interrupt(DMA1, DMA_STREAM6);
dma_disable_fifo_error_interrupt(DMA1, DMA_STREAM6);
dma_enable_transfer_complete_interrupt(DMA1, DMA_STREAM6);
}
void dma_request(void* buffer, const int datasize)
{
dma_set_memory_address(DMA1, DMA_STREAM5, (uint32_t) buffer);
dma_set_number_of_data(DMA1, DMA_STREAM5, datasize);
dma_channel_select(DMA1, DMA_STREAM5, DMA_SxCR_CHSEL_4);
dma_enable_stream(DMA1, DMA_STREAM5);
signal_host();
usart_enable_rx_dma(USART2);
}
void dma_transmit(const void* buffer, const int datasize)
{
dma_set_memory_address(DMA1, DMA_STREAM6, (uint32_t) buffer);
dma_set_number_of_data(DMA1, DMA_STREAM6, datasize);
dma_channel_select(DMA1, DMA_STREAM6, DMA_SxCR_CHSEL_4);
dma_enable_stream(DMA1, DMA_STREAM6);
usart_enable_tx_dma(USART2);
}
int dma_done(void)
{
return !((DMA1_S5CR | DMA1_S6CR) & 1);
}
void dma1_stream5_isr(void) {
usart_disable_rx_dma(USART2);
dma_clear_interrupt_flags(DMA1, DMA_STREAM5, DMA_TCIF);
dma_disable_stream(DMA1, DMA_STREAM5);
}
void dma1_stream6_isr(void) {
usart_disable_tx_dma(USART2);
dma_clear_interrupt_flags(DMA1, DMA_STREAM6, DMA_TCIF);
dma_disable_stream(DMA1, DMA_STREAM6);
}
void signal_host(void) {
usart_send_blocking(USART2, '\n');
}
int main(void)
{
clock_setup();
gpio_setup();
usart_setup(921600);
dma_transmit_setup();
dma_request_setup();
unsigned char buf[16];
dma_request(buf, 16); while (!dma_done());
buf[10] = 'd';
buf[11] = 'a';
buf[12] = 'w';
buf[13] = 'n';
dma_transmit(buf, 16); while (!dma_done());
while(1);
return 0;
}
1 ответ
Ну, я могу быть кратким об этом.
Я рекомендую против использования stty
для такого рода вещей. Я понимаю, что я, вероятно, не настроен stty
правильно, и с некоторой настройкой параметров, возможно, возможно сделать это правильно, но это совершенно неясно. Я закончил тем, что выбросил это в окно и использовал вместо этого pyserial. Я должен был сделать это несколько недель назад. Приведенный выше код STM32 работает нормально, а необходимый код Python полностью тривиален.
#!/usr/bin/env python3
import serial
dev = serial.Serial("/dev/ttyUSB0", 921600)
dev.read(1) # wait for the signal
dev.write("Defend at noon\r\n".encode('utf-8'))
while True:
x = dev.read()
print(x.decode('utf-8'), end='', flush=True)