2025-12-08 11:23 AM
Need getting-started help with STM32C011J6M6 MCU and USART1 in single-wire Async mode (using STM32Cube toolchain and C language.)
The hardware configuration is just the STM32C011J6M6 MCU (8-pin SOIC package) with one pin (pin 1) pulled up (4.7K to 3.3V). Intent is to use this in single-wire Async mode to communicate with outside world. All other MCU pins have similar pull-up resistors but are not otherwise connected. Power to the MCU for this test is furnished by the STLINK device. The test is using programmed I/O (no DMA, no interrupts.) The test program solely is written to experiment with the USART1 in single-wire mode on this device; there are no other aspects being tested or included.
Used STM32CubeMX to generate code template (targeting STM32CubeIDE), STM32CubeIDE to build the example (C-language) program, and STM32CubeProgrammer to push the resulting (elf) binary to the device using STLINK attached to the SWCLK/SWDIO and power pins.
When running, expected result is that async bytes will appear on pin 1 (active low, open drain). Actual result is that pin 1 remains held high by it's pull-up resistor.
I previously checked hardware using another short program (same tools) to set the pin as GPIO (open drain) and then toggled the pin high/low (with delay in loop) to verify electrical behavior.
Four screen grabs from STM32CubeMX are attached, as is the main.c program being used for this test (a single ZIP file contains the five small files.)
My questions (other than a plea for help getting started!) are this:
1. Is something (during initialization) not being enabled that should be enabled? (that is, does the code generated by STM32CubeMX expect the user to amend it to enable or power-up a key internal peripheral (such as Clock, GPIO or the USART)? (recall that I used the same method to verify GPIO output toggling is possible with no changes to the MX generated code other than adding the "toggle the pin" code.)
2. Does single-wire (USART) mode work when the CPU pin only has TX capability (for that peripheral)? Do I need to add/specify anything about the RX pin (which I believe is not used nor enabled when in single-wire mode)? Pin 1 offers PB7/PC14-OSCX_IN, I am using PC14 (only) which supports USART1_TX, TIM1_ETR, TIM1_BKIN2, IR_OUT, USART2_RTS_DE_CK, TIM17_CH1, TIM3_CH2, I2C1_SDA, EVENTOUT). Note that while I am using single-wire mode (which I gather is based on a configuration of USART1_TX, the pin does not offer USART1_RX capability.
3. Are the instructions HAL_HalfDuplex_EnableReceiver(&huart1); and HAL_HalfDuplex_EnableTransmitter(&huart1); actually required in this use case? My prior work (bare-metal style) with STM32F103 and single-wire USART mode didn't have any sort of enable instructions involved; once configured the USART worked as expected (for both transmit and receive) provided that the application "knew" when it was allowed to send data.
Attached (below) is a single ZIP file (the maximum attachments is 3, my file count is 5) containing four screen captures from STM32CubeMX and one copy of the generated main.c (with a few lines of application code added), as a .txt file.
Happy to provide any clarifying information as needed!
Thank you,
Dave
Solved! Go to Solution.
2025-12-10 11:13 AM
Nice you figured that out. Your code is similiar to what's generated in my HAL_UART_MspInit (Src\stm32c0xx_hal_msp.c).
KnarfB
2025-12-08 12:02 PM
Your attachment hangs in the "(Virus scan in progress ...)" loop which seems to be a recent bug in the forum setup.
You may speed up things by using the "insert code samples" button in the menu bar and copying CubeMX screenshots.
Generally speaking: Should work out-of-the box without further setup code. The relevant Ref.Man. chapter:
26.5.15 USART single-wire half-duplex communication
Single-wire half-duplex mode is selected by setting the HDSEL bit in the USART_CR3
register. In this mode, the following bits must be kept cleared:
• LINEN and CLKEN bits in the USART_CR2 register,
• SCEN and IREN bits in the USART_CR3 register.
The USART can be configured to follow a single-wire half-duplex protocol where the TX and
RX lines are internally connected. The selection between half- and Full-duplex communication is made with a control bit HDSEL in USART_CR3.
As soon as HDSEL is written to ‘1’:
• The TX and RX lines are internally connected.
• The RX pin is no longer used.
• The TX pin is always released when no data is transmitted. Thus, it acts as a standard
I/O in idle or in reception. It means that the I/O must be configured so that TX is
configured as alternate function open-drain with an external pull-up.
hth
KnarfB
2025-12-08 12:32 PM
Thanks, KnarfB.
The guidelines from the ref manual are consistent with my understanding of the expected configuration.
You've alleviated one of my concerns (would enabling instructions be required to make the internal peripherals active or behave properly.)
As I didn't specify a _RX pin at all (which would need to be on another MCU pin as _RX is not present as an option on Pin 1; the application will solely use single-wire mode), and lacking any new insight one of the questions (can the single MCU pin work properly as a single-wire without specifying the corresponding _RX pin --- STM32CubeMX didn't complain), an experiment might be to build another test code, this time with some other pin set up as _RX (that would not be used), to at least see if the issue is specific to the single-wire specification.
I also saw that the ZIP file wasn't digested (yet), thanks for the suggestion of inline, which begins below with the code generated by MX (slightly altered to include my application fragment), and continues with the four screen captures from MX that I tried to include by attachment.
Thanks again!
Dave
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint8_t buffer[8]; // Data buffer
buffer[0] = 0x55; // should create 01010101 sequence
HAL_HalfDuplex_EnableReceiver(&huart1);
HAL_HalfDuplex_EnableTransmitter(&huart1);
while (1)
{
HAL_UART_Transmit(&huart1, buffer, 1, 0xFFFF);
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_0);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV4;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 300;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_HalfDuplex_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetTxFifoThreshold(&huart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_SetRxFifoThreshold(&huart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
{
Error_Handler();
}
if (HAL_UARTEx_DisableFifoMode(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @PAram None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @PAram file: pointer to the source file name
* @PAram line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */2025-12-08 12:50 PM - edited 2025-12-08 12:51 PM
Everything looks good to me. Keep in mind that the docs say:
2025-12-08 1:49 PM
Thanks so much for cloning the project - very grateful you took the time to do that!
Your result tells me that a separate declaration for the unused _RX pin isn't the issue.
UFQFPN20 pin 20 (PC14-OSCX_IN) working also suggests to me that there's not a conflict with oscillator input alternate function.
I also figured "last wins" in the enable calls...
At this point once I get back to the workbench (likely early evening or Wednesday at the latest) I'll try generating a new project along the same lines but without the single-wire specification. From datasheet, Pin 4 can be set as PA1 which offers SPI1_SCK/I2S1_CK, USART2_RTS_DE_CK, TIM17_CH1, USART1_RX, TIM1_CH2, I2C1_SMBA, EVENTOUT. That pin is pulled high externally on the PCB. I'll also re-check that the GPIO test code I used before is still working (that's what was on the chip when I tried the code posted here, and it was working (as open drain output GPIO) at that point.
Certainly seems like this will be something obvious that I'm overlooking!
Thanks again - really appreciate the quick and extensive collaboration!
Dave
2025-12-10 9:37 AM - edited 2025-12-10 9:40 AM
Thank you again KnarfB - your workbench support of my problem illuminated the direction to proceed and saved much spinning-of-wheels!
Worked out the issue -- turns out the GPIO initialization routine generated by STM32CubeMX (at least the version I'm using) only turns on the required clock for the peripheral. I added instructions (seen below in the alternate GPIO_Init function) to support USART in 1-wire mode based on the hardware that is being used (external pull-up resistor on SO8 MCU pin 1 selected as PC14.)
static void MX_GPIO_Init_C14(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
// New code added for support of USART1_TX in 1-wire async mode
GPIO_InitTypeDef GPIO_InitStruct = {0};
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_SET);
/*Configure GPIO pin : PC14 */
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
/**/
HAL_SYSCFG_SetPinBinding(HAL_BIND_SO8_PIN1_PC14);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
2025-12-10 11:13 AM
Nice you figured that out. Your code is similiar to what's generated in my HAL_UART_MspInit (Src\stm32c0xx_hal_msp.c).
KnarfB
2025-12-10 11:44 AM
Thanks again, that will be good reference. My prior project (two years ago) was mostly bare metal and some LL code (to shoehorn about 6K lines into a 32K flash space on STM32F103), so HAL, even thought it is intended to make life easier, is for me a new environment.
I've set your reply as "accept as solution" as you've provided a wealth of useful guidance and the project is underway now... Thanks so much!